2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
165 #define B64_LINE_SIZE 57
166 #define B64_BUFFSIZE 77
168 #define MAX_REFERENCES_LEN 999
170 static GList *compose_list = NULL;
172 static Compose *compose_generic_new (PrefsAccount *account,
175 GPtrArray *attach_files,
176 GList *listAddress );
178 static Compose *compose_create (PrefsAccount *account,
183 static void compose_entry_mark_default_to (Compose *compose,
184 const gchar *address);
185 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
186 ComposeQuoteMode quote_mode,
190 static Compose *compose_forward_multiple (PrefsAccount *account,
191 GSList *msginfo_list);
192 static Compose *compose_reply (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
198 static Compose *compose_reply_mode (ComposeMode mode,
199 GSList *msginfo_list,
201 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
202 static void compose_update_privacy_systems_menu(Compose *compose);
204 static GtkWidget *compose_account_option_menu_create
206 static void compose_set_out_encoding (Compose *compose);
207 static void compose_set_template_menu (Compose *compose);
208 static void compose_template_apply (Compose *compose,
211 static void compose_destroy (Compose *compose);
213 static void compose_entries_set (Compose *compose,
215 ComposeEntryType to_type);
216 static gint compose_parse_header (Compose *compose,
218 static gchar *compose_parse_references (const gchar *ref,
221 static gchar *compose_quote_fmt (Compose *compose,
227 gboolean need_unescape,
228 const gchar *err_msg);
230 static void compose_reply_set_entry (Compose *compose,
236 followup_and_reply_to);
237 static void compose_reedit_set_entry (Compose *compose,
240 static void compose_insert_sig (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 int 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);
337 static void compose_dict_changed (void *data);
339 static void compose_add_field_list ( Compose *compose,
340 GList *listAddress );
342 /* callback functions */
344 static gboolean compose_edit_size_alloc (GtkEditable *widget,
345 GtkAllocation *allocation,
346 GtkSHRuler *shruler);
347 static void account_activated (GtkComboBox *optmenu,
349 static void attach_selected (GtkTreeView *tree_view,
350 GtkTreePath *tree_path,
351 GtkTreeViewColumn *column,
353 static gboolean attach_button_pressed (GtkWidget *widget,
354 GdkEventButton *event,
356 static gboolean attach_key_pressed (GtkWidget *widget,
359 static void compose_send_cb (GtkAction *action, gpointer data);
360 static void compose_send_later_cb (GtkAction *action, gpointer data);
362 static void compose_save_cb (GtkAction *action,
365 static void compose_attach_cb (GtkAction *action,
367 static void compose_insert_file_cb (GtkAction *action,
369 static void compose_insert_sig_cb (GtkAction *action,
372 static void compose_close_cb (GtkAction *action,
375 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
377 static void compose_address_cb (GtkAction *action,
379 static void about_show_cb (GtkAction *action,
381 static void compose_template_activate_cb(GtkWidget *widget,
384 static void compose_ext_editor_cb (GtkAction *action,
387 static gint compose_delete_cb (GtkWidget *widget,
391 static void compose_undo_cb (GtkAction *action,
393 static void compose_redo_cb (GtkAction *action,
395 static void compose_cut_cb (GtkAction *action,
397 static void compose_copy_cb (GtkAction *action,
399 static void compose_paste_cb (GtkAction *action,
401 static void compose_paste_as_quote_cb (GtkAction *action,
403 static void compose_paste_no_wrap_cb (GtkAction *action,
405 static void compose_paste_wrap_cb (GtkAction *action,
407 static void compose_allsel_cb (GtkAction *action,
410 static void compose_advanced_action_cb (GtkAction *action,
413 static void compose_grab_focus_cb (GtkWidget *widget,
416 static void compose_changed_cb (GtkTextBuffer *textbuf,
419 static void compose_wrap_cb (GtkAction *action,
421 static void compose_wrap_all_cb (GtkAction *action,
423 static void compose_find_cb (GtkAction *action,
425 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
430 static void compose_toggle_ruler_cb (GtkToggleAction *action,
432 static void compose_toggle_sign_cb (GtkToggleAction *action,
434 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
436 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
437 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
438 static void activate_privacy_system (Compose *compose,
439 PrefsAccount *account,
441 static void compose_use_signing(Compose *compose, gboolean use_signing);
442 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
443 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
445 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
447 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
449 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
451 static void compose_attach_drag_received_cb (GtkWidget *widget,
452 GdkDragContext *drag_context,
455 GtkSelectionData *data,
459 static void compose_insert_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_header_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
476 static gboolean compose_drag_drop (GtkWidget *widget,
477 GdkDragContext *drag_context,
479 guint time, gpointer user_data);
481 static void text_inserted (GtkTextBuffer *buffer,
486 static Compose *compose_generic_reply(MsgInfo *msginfo,
487 ComposeQuoteMode quote_mode,
491 gboolean followup_and_reply_to,
494 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
498 ComposeHeaderEntry *headerentry);
499 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
500 ComposeHeaderEntry *headerentry);
502 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
504 static void compose_allow_user_actions (Compose *compose, gboolean allow);
506 static void compose_nothing_cb (GtkAction *action, gpointer data)
512 static void compose_check_all (GtkAction *action, gpointer data);
513 static void compose_highlight_all (GtkAction *action, gpointer data);
514 static void compose_check_backwards (GtkAction *action, gpointer data);
515 static void compose_check_forwards_go (GtkAction *action, gpointer data);
518 static gint compose_defer_auto_save_draft (Compose *compose);
519 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
521 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
524 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
525 FolderItem *folder_item);
527 static void compose_attach_update_label(Compose *compose);
529 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
531 static GtkActionEntry compose_popup_entries[] =
533 {"Compose", NULL, "Compose" },
534 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
535 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
536 {"Compose/---", NULL, "---", NULL, NULL, NULL },
537 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
540 static GtkActionEntry compose_entries[] =
542 {"Menu", NULL, "Menu" },
544 {"Message", NULL, N_("_Message") },
545 {"Edit", NULL, N_("_Edit") },
547 {"Spelling", NULL, N_("_Spelling") },
549 {"Options", NULL, N_("_Options") },
550 {"Tools", NULL, N_("_Tools") },
551 {"Help", NULL, N_("_Help") },
553 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
554 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
555 {"Message/---", NULL, "---" },
557 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
558 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
559 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
562 /* {"Message/---", NULL, "---" }, */
563 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
566 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
567 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
568 {"Edit/---", NULL, "---" },
570 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
571 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
572 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
574 {"Edit/SpecialPaste", NULL, N_("Special paste") },
575 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
576 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
577 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
579 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
581 {"Edit/Advanced", NULL, N_("A_dvanced") },
582 {"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*/
583 {"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*/
584 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
585 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"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*/
590 {"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*/
591 {"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*/
592 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
593 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
594 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
595 {"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*/
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
602 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
607 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
608 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
609 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
610 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
612 {"Spelling/---", NULL, "---" },
613 {"Spelling/Options", NULL, N_("_Options") },
618 {"Options/ReplyMode", NULL, N_("Reply _mode") },
619 {"Options/---", NULL, "---" },
620 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
621 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
623 /* {"Options/---", NULL, "---" }, */
625 {"Options/Priority", NULL, N_("_Priority") },
627 {"Options/Encoding", NULL, N_("Character _encoding") },
628 {"Options/Encoding/---", NULL, "---" },
629 #define ENC_ACTION(cs_char,c_char,string) \
630 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
632 {"Options/Encoding/Western", NULL, N_("Western European") },
633 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
634 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
635 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
636 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
637 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
638 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
639 {"Options/Encoding/Korean", NULL, N_("Korean") },
640 {"Options/Encoding/Thai", NULL, N_("Thai") },
643 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
645 {"Tools/Template", NULL, N_("_Template") },
646 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Tools/Actions", NULL, N_("Actio_ns") },
648 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
651 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
654 static GtkToggleActionEntry compose_toggle_entries[] =
656 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
657 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
658 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
659 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
660 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
661 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
662 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
665 static GtkRadioActionEntry compose_radio_rm_entries[] =
667 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
669 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
670 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
673 static GtkRadioActionEntry compose_radio_prio_entries[] =
675 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
678 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
679 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
682 static GtkRadioActionEntry compose_radio_enc_entries[] =
684 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
717 static GtkTargetEntry compose_mime_types[] =
719 {"text/uri-list", 0, 0},
720 {"UTF8_STRING", 0, 0},
724 static gboolean compose_put_existing_to_front(MsgInfo *info)
726 GList *compose_list = compose_get_compose_list();
730 for (elem = compose_list; elem != NULL && elem->data != NULL;
732 Compose *c = (Compose*)elem->data;
734 if (!c->targetinfo || !c->targetinfo->msgid ||
738 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
739 gtkut_window_popup(c->window);
747 static GdkColor quote_color1 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color2 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_color3 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor1 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor2 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_bgcolor3 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor signature_color = {
768 static GdkColor uri_color = {
775 static void compose_create_tags(GtkTextView *text, Compose *compose)
777 GtkTextBuffer *buffer;
778 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 buffer = gtk_text_view_get_buffer(text);
786 if (prefs_common.enable_color) {
787 /* grab the quote colors, converting from an int to a GdkColor */
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
800 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
805 signature_color = quote_color1 = quote_color2 = quote_color3 =
806 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
809 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
810 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
811 "foreground-gdk", "e_color1,
812 "paragraph-background-gdk", "e_bgcolor1,
814 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
815 "foreground-gdk", "e_color2,
816 "paragraph-background-gdk", "e_bgcolor2,
818 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
819 "foreground-gdk", "e_color3,
820 "paragraph-background-gdk", "e_bgcolor3,
823 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
824 "foreground-gdk", "e_color1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
834 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
835 "foreground-gdk", &signature_color,
838 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
839 "foreground-gdk", &uri_color,
841 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
842 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
844 color[0] = quote_color1;
845 color[1] = quote_color2;
846 color[2] = quote_color3;
847 color[3] = quote_bgcolor1;
848 color[4] = quote_bgcolor2;
849 color[5] = quote_bgcolor3;
850 color[6] = signature_color;
851 color[7] = uri_color;
852 cmap = gdk_drawable_get_colormap(compose->window->window);
853 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
855 for (i = 0; i < 8; i++) {
856 if (success[i] == FALSE) {
859 g_warning("Compose: color allocation failed.\n");
860 style = gtk_widget_get_style(GTK_WIDGET(text));
861 quote_color1 = quote_color2 = quote_color3 =
862 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
863 signature_color = uri_color = black;
868 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
869 GPtrArray *attach_files)
871 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
874 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
876 return compose_generic_new(account, mailto, item, NULL, NULL);
879 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
881 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
884 #define SCROLL_TO_CURSOR(compose) { \
885 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
886 gtk_text_view_get_buffer( \
887 GTK_TEXT_VIEW(compose->text))); \
888 gtk_text_view_scroll_mark_onscreen( \
889 GTK_TEXT_VIEW(compose->text), \
893 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
896 if (folderidentifier) {
897 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
898 prefs_common.compose_save_to_history = add_history(
899 prefs_common.compose_save_to_history, folderidentifier);
900 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
901 prefs_common.compose_save_to_history);
904 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
905 if (folderidentifier)
906 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
908 gtk_entry_set_text(GTK_ENTRY(entry), "");
911 static gchar *compose_get_save_to(Compose *compose)
914 gchar *result = NULL;
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 result = gtk_editable_get_chars(entry, 0, -1);
919 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
920 prefs_common.compose_save_to_history = add_history(
921 prefs_common.compose_save_to_history, result);
922 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
923 prefs_common.compose_save_to_history);
928 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
929 GPtrArray *attach_files, GList *listAddress )
932 GtkTextView *textview;
933 GtkTextBuffer *textbuf;
935 const gchar *subject_format = NULL;
936 const gchar *body_format = NULL;
937 gchar *mailto_from = NULL;
938 PrefsAccount *mailto_account = NULL;
939 MsgInfo* dummyinfo = NULL;
941 /* check if mailto defines a from */
942 if (mailto && *mailto != '\0') {
943 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
944 /* mailto defines a from, check if we can get account prefs from it,
945 if not, the account prefs will be guessed using other ways, but we'll keep
948 mailto_account = account_find_from_address(mailto_from, TRUE);
950 account = mailto_account;
953 /* if no account prefs set from mailto, set if from folder prefs (if any) */
954 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
955 account = account_find_from_id(item->prefs->default_account);
957 /* if no account prefs set, fallback to the current one */
958 if (!account) account = cur_account;
959 cm_return_val_if_fail(account != NULL, NULL);
961 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
963 /* override from name if mailto asked for it */
965 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
968 /* override from name according to folder properties */
969 if (item && item->prefs &&
970 item->prefs->compose_with_format &&
971 item->prefs->compose_override_from_format &&
972 *item->prefs->compose_override_from_format != '\0') {
977 dummyinfo = compose_msginfo_new_from_compose(compose);
979 /* decode \-escape sequences in the internal representation of the quote format */
980 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
981 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
984 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
987 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
989 quote_fmt_scan_string(tmp);
992 buf = quote_fmt_get_buffer();
994 alertpanel_error(_("New message From format error."));
996 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
997 quote_fmt_reset_vartable();
1002 compose->replyinfo = NULL;
1003 compose->fwdinfo = NULL;
1005 textview = GTK_TEXT_VIEW(compose->text);
1006 textbuf = gtk_text_view_get_buffer(textview);
1007 compose_create_tags(textview, compose);
1009 undo_block(compose->undostruct);
1011 compose_set_dictionaries_from_folder_prefs(compose, item);
1014 if (account->auto_sig)
1015 compose_insert_sig(compose, FALSE);
1016 gtk_text_buffer_get_start_iter(textbuf, &iter);
1017 gtk_text_buffer_place_cursor(textbuf, &iter);
1019 if (account->protocol != A_NNTP) {
1020 if (mailto && *mailto != '\0') {
1021 compose_entries_set(compose, mailto, COMPOSE_TO);
1023 } else if (item && item->prefs) {
1024 if (item->prefs->enable_default_bcc) {
1025 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1027 if (item->prefs->enable_default_cc) {
1028 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1030 if (item->prefs->enable_default_replyto) {
1031 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1033 if (item->prefs->enable_default_to) {
1034 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1035 compose_entry_mark_default_to(compose, item->prefs->default_to);
1038 if (item && item->ret_rcpt) {
1039 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1042 if (mailto && *mailto != '\0') {
1043 if (!strchr(mailto, '@'))
1044 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1046 compose_entries_set(compose, mailto, COMPOSE_TO);
1047 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1051 * CLAWS: just don't allow return receipt request, even if the user
1052 * may want to send an email. simple but foolproof.
1054 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1056 compose_add_field_list( compose, listAddress );
1058 if (item && item->prefs && item->prefs->compose_with_format) {
1059 subject_format = item->prefs->compose_subject_format;
1060 body_format = item->prefs->compose_body_format;
1061 } else if (account->compose_with_format) {
1062 subject_format = account->compose_subject_format;
1063 body_format = account->compose_body_format;
1064 } else if (prefs_common.compose_with_format) {
1065 subject_format = prefs_common.compose_subject_format;
1066 body_format = prefs_common.compose_body_format;
1069 if (subject_format || body_format) {
1072 && *subject_format != '\0' )
1074 gchar *subject = NULL;
1079 dummyinfo = compose_msginfo_new_from_compose(compose);
1081 /* decode \-escape sequences in the internal representation of the quote format */
1082 tmp = malloc(strlen(subject_format)+1);
1083 pref_get_unescaped_pref(tmp, subject_format);
1085 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1087 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1088 compose->gtkaspell);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1092 quote_fmt_scan_string(tmp);
1095 buf = quote_fmt_get_buffer();
1097 alertpanel_error(_("New message subject format error."));
1099 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1100 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1101 quote_fmt_reset_vartable();
1108 && *body_format != '\0' )
1111 GtkTextBuffer *buffer;
1112 GtkTextIter start, end;
1116 dummyinfo = compose_msginfo_new_from_compose(compose);
1118 text = GTK_TEXT_VIEW(compose->text);
1119 buffer = gtk_text_view_get_buffer(text);
1120 gtk_text_buffer_get_start_iter(buffer, &start);
1121 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1122 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1124 compose_quote_fmt(compose, dummyinfo,
1126 NULL, tmp, FALSE, TRUE,
1127 _("New message body format error at line %d."));
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1135 procmsg_msginfo_free( dummyinfo );
1141 for (i = 0; i < attach_files->len; i++) {
1142 file = g_ptr_array_index(attach_files, i);
1143 compose_attach_append(compose, file, file, NULL);
1147 compose_show_first_last_header(compose, TRUE);
1149 /* Set save folder */
1150 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1151 gchar *folderidentifier;
1153 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1154 folderidentifier = folder_item_get_identifier(item);
1155 compose_set_save_to(compose, folderidentifier);
1156 g_free(folderidentifier);
1159 gtk_widget_grab_focus(compose->header_last->entry);
1161 undo_unblock(compose->undostruct);
1163 if (prefs_common.auto_exteditor)
1164 compose_exec_ext_editor(compose);
1166 compose->draft_timeout_tag = -1;
1167 SCROLL_TO_CURSOR(compose);
1169 compose->modified = FALSE;
1170 compose_set_title(compose);
1174 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1175 gboolean override_pref, const gchar *system)
1177 const gchar *privacy = NULL;
1179 cm_return_if_fail(compose != NULL);
1180 cm_return_if_fail(account != NULL);
1182 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1187 else if (account->default_privacy_system
1188 && strlen(account->default_privacy_system)) {
1189 privacy = account->default_privacy_system;
1191 GSList *privacy_avail = privacy_get_system_ids();
1192 if (privacy_avail && g_slist_length(privacy_avail)) {
1193 privacy = (gchar *)(privacy_avail->data);
1196 if (privacy != NULL) {
1198 g_free(compose->privacy_system);
1199 compose->privacy_system = NULL;
1201 if (compose->privacy_system == NULL)
1202 compose->privacy_system = g_strdup(privacy);
1203 else if (*(compose->privacy_system) == '\0') {
1204 g_free(compose->privacy_system);
1205 compose->privacy_system = g_strdup(privacy);
1207 compose_update_privacy_system_menu_item(compose, FALSE);
1208 compose_use_encryption(compose, TRUE);
1212 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1214 const gchar *privacy = NULL;
1218 else if (account->default_privacy_system
1219 && strlen(account->default_privacy_system)) {
1220 privacy = account->default_privacy_system;
1222 GSList *privacy_avail = privacy_get_system_ids();
1223 if (privacy_avail && g_slist_length(privacy_avail)) {
1224 privacy = (gchar *)(privacy_avail->data);
1228 if (privacy != NULL) {
1230 g_free(compose->privacy_system);
1231 compose->privacy_system = NULL;
1233 if (compose->privacy_system == NULL)
1234 compose->privacy_system = g_strdup(privacy);
1235 compose_update_privacy_system_menu_item(compose, FALSE);
1236 compose_use_signing(compose, TRUE);
1240 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1244 Compose *compose = NULL;
1246 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1248 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1249 cm_return_val_if_fail(msginfo != NULL, NULL);
1251 list_len = g_slist_length(msginfo_list);
1255 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1256 FALSE, prefs_common.default_reply_list, FALSE, body);
1258 case COMPOSE_REPLY_WITH_QUOTE:
1259 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1260 FALSE, prefs_common.default_reply_list, FALSE, body);
1262 case COMPOSE_REPLY_WITHOUT_QUOTE:
1263 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1264 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1266 case COMPOSE_REPLY_TO_SENDER:
1267 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1268 FALSE, FALSE, TRUE, body);
1270 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1271 compose = compose_followup_and_reply_to(msginfo,
1272 COMPOSE_QUOTE_CHECK,
1273 FALSE, FALSE, body);
1275 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1277 FALSE, FALSE, TRUE, body);
1279 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1280 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1281 FALSE, FALSE, TRUE, NULL);
1283 case COMPOSE_REPLY_TO_ALL:
1284 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1285 TRUE, FALSE, FALSE, body);
1287 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1288 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1289 TRUE, FALSE, FALSE, body);
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1293 TRUE, FALSE, FALSE, NULL);
1295 case COMPOSE_REPLY_TO_LIST:
1296 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1297 FALSE, TRUE, FALSE, body);
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1301 FALSE, TRUE, FALSE, body);
1303 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1304 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1305 FALSE, TRUE, FALSE, NULL);
1307 case COMPOSE_FORWARD:
1308 if (prefs_common.forward_as_attachment) {
1309 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1312 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1316 case COMPOSE_FORWARD_INLINE:
1317 /* check if we reply to more than one Message */
1318 if (list_len == 1) {
1319 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1322 /* more messages FALL THROUGH */
1323 case COMPOSE_FORWARD_AS_ATTACH:
1324 compose = compose_forward_multiple(NULL, msginfo_list);
1326 case COMPOSE_REDIRECT:
1327 compose = compose_redirect(NULL, msginfo, FALSE);
1330 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1333 if (compose == NULL) {
1334 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1338 compose->rmode = mode;
1339 switch (compose->rmode) {
1341 case COMPOSE_REPLY_WITH_QUOTE:
1342 case COMPOSE_REPLY_WITHOUT_QUOTE:
1343 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1344 debug_print("reply mode Normal\n");
1345 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1346 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1348 case COMPOSE_REPLY_TO_SENDER:
1349 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1350 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1351 debug_print("reply mode Sender\n");
1352 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1354 case COMPOSE_REPLY_TO_ALL:
1355 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1356 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1357 debug_print("reply mode All\n");
1358 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1360 case COMPOSE_REPLY_TO_LIST:
1361 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1362 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1363 debug_print("reply mode List\n");
1364 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1372 static Compose *compose_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1379 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1380 to_sender, FALSE, body);
1383 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1384 ComposeQuoteMode quote_mode,
1389 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1390 to_sender, TRUE, body);
1393 static void compose_extract_original_charset(Compose *compose)
1395 MsgInfo *info = NULL;
1396 if (compose->replyinfo) {
1397 info = compose->replyinfo;
1398 } else if (compose->fwdinfo) {
1399 info = compose->fwdinfo;
1400 } else if (compose->targetinfo) {
1401 info = compose->targetinfo;
1404 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1405 MimeInfo *partinfo = mimeinfo;
1406 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1407 partinfo = procmime_mimeinfo_next(partinfo);
1409 compose->orig_charset =
1410 g_strdup(procmime_mimeinfo_get_parameter(
1411 partinfo, "charset"));
1413 procmime_mimeinfo_free_all(mimeinfo);
1417 #define SIGNAL_BLOCK(buffer) { \
1418 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1419 G_CALLBACK(compose_changed_cb), \
1421 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1422 G_CALLBACK(text_inserted), \
1426 #define SIGNAL_UNBLOCK(buffer) { \
1427 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1428 G_CALLBACK(compose_changed_cb), \
1430 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1431 G_CALLBACK(text_inserted), \
1435 static Compose *compose_generic_reply(MsgInfo *msginfo,
1436 ComposeQuoteMode quote_mode,
1437 gboolean to_all, gboolean to_ml,
1439 gboolean followup_and_reply_to,
1443 PrefsAccount *account = NULL;
1444 GtkTextView *textview;
1445 GtkTextBuffer *textbuf;
1446 gboolean quote = FALSE;
1447 const gchar *qmark = NULL;
1448 const gchar *body_fmt = NULL;
1449 gchar *s_system = NULL;
1451 cm_return_val_if_fail(msginfo != NULL, NULL);
1452 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1454 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1456 cm_return_val_if_fail(account != NULL, NULL);
1458 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1460 compose->updating = TRUE;
1462 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1465 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1466 if (!compose->replyinfo)
1467 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1469 compose_extract_original_charset(compose);
1471 if (msginfo->folder && msginfo->folder->ret_rcpt)
1472 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1474 /* Set save folder */
1475 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1476 gchar *folderidentifier;
1478 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1479 folderidentifier = folder_item_get_identifier(msginfo->folder);
1480 compose_set_save_to(compose, folderidentifier);
1481 g_free(folderidentifier);
1484 if (compose_parse_header(compose, msginfo) < 0) {
1485 compose->updating = FALSE;
1486 compose_destroy(compose);
1490 /* override from name according to folder properties */
1491 if (msginfo->folder && msginfo->folder->prefs &&
1492 msginfo->folder->prefs->reply_with_format &&
1493 msginfo->folder->prefs->reply_override_from_format &&
1494 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1499 /* decode \-escape sequences in the internal representation of the quote format */
1500 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1501 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1504 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1505 compose->gtkaspell);
1507 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1509 quote_fmt_scan_string(tmp);
1512 buf = quote_fmt_get_buffer();
1514 alertpanel_error(_("Message reply From format error."));
1516 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1517 quote_fmt_reset_vartable();
1522 textview = (GTK_TEXT_VIEW(compose->text));
1523 textbuf = gtk_text_view_get_buffer(textview);
1524 compose_create_tags(textview, compose);
1526 undo_block(compose->undostruct);
1528 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1531 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1532 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1533 /* use the reply format of folder (if enabled), or the account's one
1534 (if enabled) or fallback to the global reply format, which is always
1535 enabled (even if empty), and use the relevant quotemark */
1537 if (msginfo->folder && msginfo->folder->prefs &&
1538 msginfo->folder->prefs->reply_with_format) {
1539 qmark = msginfo->folder->prefs->reply_quotemark;
1540 body_fmt = msginfo->folder->prefs->reply_body_format;
1542 } else if (account->reply_with_format) {
1543 qmark = account->reply_quotemark;
1544 body_fmt = account->reply_body_format;
1547 qmark = prefs_common.quotemark;
1548 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1549 body_fmt = gettext(prefs_common.quotefmt);
1556 /* empty quotemark is not allowed */
1557 if (qmark == NULL || *qmark == '\0')
1559 compose_quote_fmt(compose, compose->replyinfo,
1560 body_fmt, qmark, body, FALSE, TRUE,
1561 _("Message reply format error at line %d."));
1562 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1563 quote_fmt_reset_vartable();
1566 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1567 compose_force_encryption(compose, account, FALSE, s_system);
1570 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1571 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1572 compose_force_signing(compose, account, s_system);
1576 SIGNAL_BLOCK(textbuf);
1578 if (account->auto_sig)
1579 compose_insert_sig(compose, FALSE);
1581 compose_wrap_all(compose);
1583 SIGNAL_UNBLOCK(textbuf);
1585 gtk_widget_grab_focus(compose->text);
1587 undo_unblock(compose->undostruct);
1589 if (prefs_common.auto_exteditor)
1590 compose_exec_ext_editor(compose);
1592 compose->modified = FALSE;
1593 compose_set_title(compose);
1595 compose->updating = FALSE;
1596 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1597 SCROLL_TO_CURSOR(compose);
1599 if (compose->deferred_destroy) {
1600 compose_destroy(compose);
1607 #define INSERT_FW_HEADER(var, hdr) \
1608 if (msginfo->var && *msginfo->var) { \
1609 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1610 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1611 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1614 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1615 gboolean as_attach, const gchar *body,
1616 gboolean no_extedit,
1620 GtkTextView *textview;
1621 GtkTextBuffer *textbuf;
1624 cm_return_val_if_fail(msginfo != NULL, NULL);
1625 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1628 !(account = compose_guess_forward_account_from_msginfo
1630 account = cur_account;
1632 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1634 compose->updating = TRUE;
1635 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1636 if (!compose->fwdinfo)
1637 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1639 compose_extract_original_charset(compose);
1641 if (msginfo->subject && *msginfo->subject) {
1642 gchar *buf, *buf2, *p;
1644 buf = p = g_strdup(msginfo->subject);
1645 p += subject_get_prefix_length(p);
1646 memmove(buf, p, strlen(p) + 1);
1648 buf2 = g_strdup_printf("Fw: %s", buf);
1649 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1655 /* override from name according to folder properties */
1656 if (msginfo->folder && msginfo->folder->prefs &&
1657 msginfo->folder->prefs->forward_with_format &&
1658 msginfo->folder->prefs->forward_override_from_format &&
1659 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1663 MsgInfo *full_msginfo = NULL;
1666 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1668 full_msginfo = procmsg_msginfo_copy(msginfo);
1670 /* decode \-escape sequences in the internal representation of the quote format */
1671 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1672 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1675 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1676 compose->gtkaspell);
1678 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1680 quote_fmt_scan_string(tmp);
1683 buf = quote_fmt_get_buffer();
1685 alertpanel_error(_("Message forward From format error."));
1687 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1688 quote_fmt_reset_vartable();
1691 procmsg_msginfo_free(full_msginfo);
1694 textview = GTK_TEXT_VIEW(compose->text);
1695 textbuf = gtk_text_view_get_buffer(textview);
1696 compose_create_tags(textview, compose);
1698 undo_block(compose->undostruct);
1702 msgfile = procmsg_get_message_file(msginfo);
1703 if (!is_file_exist(msgfile))
1704 g_warning("%s: file not exist\n", msgfile);
1706 compose_attach_append(compose, msgfile, msgfile,
1711 const gchar *qmark = NULL;
1712 const gchar *body_fmt = NULL;
1713 MsgInfo *full_msginfo;
1715 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1716 body_fmt = gettext(prefs_common.fw_quotefmt);
1720 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1722 full_msginfo = procmsg_msginfo_copy(msginfo);
1724 /* use the forward format of folder (if enabled), or the account's one
1725 (if enabled) or fallback to the global forward format, which is always
1726 enabled (even if empty), and use the relevant quotemark */
1727 if (msginfo->folder && msginfo->folder->prefs &&
1728 msginfo->folder->prefs->forward_with_format) {
1729 qmark = msginfo->folder->prefs->forward_quotemark;
1730 body_fmt = msginfo->folder->prefs->forward_body_format;
1732 } else if (account->forward_with_format) {
1733 qmark = account->forward_quotemark;
1734 body_fmt = account->forward_body_format;
1737 qmark = prefs_common.fw_quotemark;
1738 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1739 body_fmt = gettext(prefs_common.fw_quotefmt);
1744 /* empty quotemark is not allowed */
1745 if (qmark == NULL || *qmark == '\0')
1748 compose_quote_fmt(compose, full_msginfo,
1749 body_fmt, qmark, body, FALSE, TRUE,
1750 _("Message forward format error at line %d."));
1751 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1752 quote_fmt_reset_vartable();
1753 compose_attach_parts(compose, msginfo);
1755 procmsg_msginfo_free(full_msginfo);
1758 SIGNAL_BLOCK(textbuf);
1760 if (account->auto_sig)
1761 compose_insert_sig(compose, FALSE);
1763 compose_wrap_all(compose);
1765 SIGNAL_UNBLOCK(textbuf);
1767 gtk_text_buffer_get_start_iter(textbuf, &iter);
1768 gtk_text_buffer_place_cursor(textbuf, &iter);
1770 gtk_widget_grab_focus(compose->header_last->entry);
1772 if (!no_extedit && prefs_common.auto_exteditor)
1773 compose_exec_ext_editor(compose);
1776 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1777 gchar *folderidentifier;
1779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1780 folderidentifier = folder_item_get_identifier(msginfo->folder);
1781 compose_set_save_to(compose, folderidentifier);
1782 g_free(folderidentifier);
1785 undo_unblock(compose->undostruct);
1787 compose->modified = FALSE;
1788 compose_set_title(compose);
1790 compose->updating = FALSE;
1791 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1792 SCROLL_TO_CURSOR(compose);
1794 if (compose->deferred_destroy) {
1795 compose_destroy(compose);
1802 #undef INSERT_FW_HEADER
1804 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1807 GtkTextView *textview;
1808 GtkTextBuffer *textbuf;
1812 gboolean single_mail = TRUE;
1814 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1816 if (g_slist_length(msginfo_list) > 1)
1817 single_mail = FALSE;
1819 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1820 if (((MsgInfo *)msginfo->data)->folder == NULL)
1823 /* guess account from first selected message */
1825 !(account = compose_guess_forward_account_from_msginfo
1826 (msginfo_list->data)))
1827 account = cur_account;
1829 cm_return_val_if_fail(account != NULL, NULL);
1831 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1832 if (msginfo->data) {
1833 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1834 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1838 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1839 g_warning("no msginfo_list");
1843 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1845 compose->updating = TRUE;
1847 /* override from name according to folder properties */
1848 if (msginfo_list->data) {
1849 MsgInfo *msginfo = msginfo_list->data;
1851 if (msginfo->folder && msginfo->folder->prefs &&
1852 msginfo->folder->prefs->forward_with_format &&
1853 msginfo->folder->prefs->forward_override_from_format &&
1854 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1859 /* decode \-escape sequences in the internal representation of the quote format */
1860 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1861 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1864 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1865 compose->gtkaspell);
1867 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1869 quote_fmt_scan_string(tmp);
1872 buf = quote_fmt_get_buffer();
1874 alertpanel_error(_("Message forward From format error."));
1876 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1877 quote_fmt_reset_vartable();
1883 textview = GTK_TEXT_VIEW(compose->text);
1884 textbuf = gtk_text_view_get_buffer(textview);
1885 compose_create_tags(textview, compose);
1887 undo_block(compose->undostruct);
1888 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1889 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1891 if (!is_file_exist(msgfile))
1892 g_warning("%s: file not exist\n", msgfile);
1894 compose_attach_append(compose, msgfile, msgfile,
1900 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1901 if (info->subject && *info->subject) {
1902 gchar *buf, *buf2, *p;
1904 buf = p = g_strdup(info->subject);
1905 p += subject_get_prefix_length(p);
1906 memmove(buf, p, strlen(p) + 1);
1908 buf2 = g_strdup_printf("Fw: %s", buf);
1909 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1915 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1916 _("Fw: multiple emails"));
1919 SIGNAL_BLOCK(textbuf);
1921 if (account->auto_sig)
1922 compose_insert_sig(compose, FALSE);
1924 compose_wrap_all(compose);
1926 SIGNAL_UNBLOCK(textbuf);
1928 gtk_text_buffer_get_start_iter(textbuf, &iter);
1929 gtk_text_buffer_place_cursor(textbuf, &iter);
1931 gtk_widget_grab_focus(compose->header_last->entry);
1932 undo_unblock(compose->undostruct);
1933 compose->modified = FALSE;
1934 compose_set_title(compose);
1936 compose->updating = FALSE;
1937 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1938 SCROLL_TO_CURSOR(compose);
1940 if (compose->deferred_destroy) {
1941 compose_destroy(compose);
1948 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1950 GtkTextIter start = *iter;
1951 GtkTextIter end_iter;
1952 int start_pos = gtk_text_iter_get_offset(&start);
1954 if (!compose->account->sig_sep)
1957 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1958 start_pos+strlen(compose->account->sig_sep));
1960 /* check sig separator */
1961 str = gtk_text_iter_get_text(&start, &end_iter);
1962 if (!strcmp(str, compose->account->sig_sep)) {
1964 /* check end of line (\n) */
1965 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1966 start_pos+strlen(compose->account->sig_sep));
1967 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1968 start_pos+strlen(compose->account->sig_sep)+1);
1969 tmp = gtk_text_iter_get_text(&start, &end_iter);
1970 if (!strcmp(tmp,"\n")) {
1982 static void compose_colorize_signature(Compose *compose)
1984 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1986 GtkTextIter end_iter;
1987 gtk_text_buffer_get_start_iter(buffer, &iter);
1988 while (gtk_text_iter_forward_line(&iter))
1989 if (compose_is_sig_separator(compose, buffer, &iter)) {
1990 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1991 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1995 #define BLOCK_WRAP() { \
1996 prev_autowrap = compose->autowrap; \
1997 buffer = gtk_text_view_get_buffer( \
1998 GTK_TEXT_VIEW(compose->text)); \
1999 compose->autowrap = FALSE; \
2001 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2002 G_CALLBACK(compose_changed_cb), \
2004 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2005 G_CALLBACK(text_inserted), \
2008 #define UNBLOCK_WRAP() { \
2009 compose->autowrap = prev_autowrap; \
2010 if (compose->autowrap) { \
2011 gint old = compose->draft_timeout_tag; \
2012 compose->draft_timeout_tag = -2; \
2013 compose_wrap_all(compose); \
2014 compose->draft_timeout_tag = old; \
2017 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2018 G_CALLBACK(compose_changed_cb), \
2020 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2021 G_CALLBACK(text_inserted), \
2025 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2027 Compose *compose = NULL;
2028 PrefsAccount *account = NULL;
2029 GtkTextView *textview;
2030 GtkTextBuffer *textbuf;
2034 gchar buf[BUFFSIZE];
2035 gboolean use_signing = FALSE;
2036 gboolean use_encryption = FALSE;
2037 gchar *privacy_system = NULL;
2038 int priority = PRIORITY_NORMAL;
2039 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2040 gboolean autowrap = prefs_common.autowrap;
2041 gboolean autoindent = prefs_common.auto_indent;
2043 cm_return_val_if_fail(msginfo != NULL, NULL);
2044 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2046 if (compose_put_existing_to_front(msginfo)) {
2050 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2051 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2052 gchar queueheader_buf[BUFFSIZE];
2055 /* Select Account from queue headers */
2056 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2057 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2058 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2059 account = account_find_from_id(id);
2061 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2062 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2063 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2064 account = account_find_from_id(id);
2066 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2067 sizeof(queueheader_buf), "NAID:")) {
2068 id = atoi(&queueheader_buf[strlen("NAID:")]);
2069 account = account_find_from_id(id);
2071 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2072 sizeof(queueheader_buf), "MAID:")) {
2073 id = atoi(&queueheader_buf[strlen("MAID:")]);
2074 account = account_find_from_id(id);
2076 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2077 sizeof(queueheader_buf), "S:")) {
2078 account = account_find_from_address(queueheader_buf, FALSE);
2080 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2081 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2082 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2083 use_signing = param;
2086 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2087 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2088 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2089 use_signing = param;
2092 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2093 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2094 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2095 use_encryption = param;
2097 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2098 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2099 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2100 use_encryption = param;
2102 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2103 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2104 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2107 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2108 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2109 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2112 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2113 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2114 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2116 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2118 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2121 sizeof(queueheader_buf), "X-Priority: ")) {
2122 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2125 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2126 sizeof(queueheader_buf), "RMID:")) {
2127 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2128 if (tokens[0] && tokens[1] && tokens[2]) {
2129 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2130 if (orig_item != NULL) {
2131 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2136 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "FMID:")) {
2138 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2139 if (tokens[0] && tokens[1] && tokens[2]) {
2140 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2141 if (orig_item != NULL) {
2142 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2148 account = msginfo->folder->folder->account;
2151 if (!account && prefs_common.reedit_account_autosel) {
2152 gchar from[BUFFSIZE];
2153 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2154 extract_address(from);
2155 account = account_find_from_address(from, FALSE);
2159 account = cur_account;
2161 cm_return_val_if_fail(account != NULL, NULL);
2163 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2165 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2166 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2167 compose->autowrap = autowrap;
2168 compose->replyinfo = replyinfo;
2169 compose->fwdinfo = fwdinfo;
2171 compose->updating = TRUE;
2172 compose->priority = priority;
2174 if (privacy_system != NULL) {
2175 compose->privacy_system = privacy_system;
2176 compose_use_signing(compose, use_signing);
2177 compose_use_encryption(compose, use_encryption);
2178 compose_update_privacy_system_menu_item(compose, FALSE);
2180 activate_privacy_system(compose, account, FALSE);
2183 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2185 compose_extract_original_charset(compose);
2187 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2188 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2189 gchar queueheader_buf[BUFFSIZE];
2191 /* Set message save folder */
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2194 compose_set_save_to(compose, &queueheader_buf[4]);
2196 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2197 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2199 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2204 if (compose_parse_header(compose, msginfo) < 0) {
2205 compose->updating = FALSE;
2206 compose_destroy(compose);
2209 compose_reedit_set_entry(compose, msginfo);
2211 textview = GTK_TEXT_VIEW(compose->text);
2212 textbuf = gtk_text_view_get_buffer(textview);
2213 compose_create_tags(textview, compose);
2215 mark = gtk_text_buffer_get_insert(textbuf);
2216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2218 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2219 G_CALLBACK(compose_changed_cb),
2222 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2223 fp = procmime_get_first_encrypted_text_content(msginfo);
2225 compose_force_encryption(compose, account, TRUE, NULL);
2228 fp = procmime_get_first_text_content(msginfo);
2231 g_warning("Can't get text part\n");
2235 gboolean prev_autowrap = compose->autowrap;
2236 GtkTextBuffer *buffer = textbuf;
2238 while (fgets(buf, sizeof(buf), fp) != NULL) {
2240 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2246 compose_attach_parts(compose, msginfo);
2248 compose_colorize_signature(compose);
2250 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2251 G_CALLBACK(compose_changed_cb),
2254 gtk_widget_grab_focus(compose->text);
2256 if (prefs_common.auto_exteditor) {
2257 compose_exec_ext_editor(compose);
2259 compose->modified = FALSE;
2260 compose_set_title(compose);
2262 compose->updating = FALSE;
2263 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2264 SCROLL_TO_CURSOR(compose);
2266 if (compose->deferred_destroy) {
2267 compose_destroy(compose);
2271 compose->sig_str = account_get_signature_str(compose->account);
2276 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2283 cm_return_val_if_fail(msginfo != NULL, NULL);
2286 account = account_get_reply_account(msginfo,
2287 prefs_common.reply_account_autosel);
2288 cm_return_val_if_fail(account != NULL, NULL);
2290 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2292 compose->updating = TRUE;
2294 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2295 compose->replyinfo = NULL;
2296 compose->fwdinfo = NULL;
2298 compose_show_first_last_header(compose, TRUE);
2300 gtk_widget_grab_focus(compose->header_last->entry);
2302 filename = procmsg_get_message_file(msginfo);
2304 if (filename == NULL) {
2305 compose->updating = FALSE;
2306 compose_destroy(compose);
2311 compose->redirect_filename = filename;
2313 /* Set save folder */
2314 item = msginfo->folder;
2315 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2316 gchar *folderidentifier;
2318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2319 folderidentifier = folder_item_get_identifier(item);
2320 compose_set_save_to(compose, folderidentifier);
2321 g_free(folderidentifier);
2324 compose_attach_parts(compose, msginfo);
2326 if (msginfo->subject)
2327 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2329 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2331 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2332 _("Message redirect format error at line %d."));
2333 quote_fmt_reset_vartable();
2334 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2336 compose_colorize_signature(compose);
2339 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2340 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2348 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2349 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2350 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2352 if (compose->toolbar->draft_btn)
2353 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2354 if (compose->toolbar->insert_btn)
2355 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2356 if (compose->toolbar->attach_btn)
2357 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2358 if (compose->toolbar->sig_btn)
2359 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2360 if (compose->toolbar->exteditor_btn)
2361 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2362 if (compose->toolbar->linewrap_current_btn)
2363 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2364 if (compose->toolbar->linewrap_all_btn)
2365 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2367 compose->modified = FALSE;
2368 compose_set_title(compose);
2369 compose->updating = FALSE;
2370 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2371 SCROLL_TO_CURSOR(compose);
2373 if (compose->deferred_destroy) {
2374 compose_destroy(compose);
2381 GList *compose_get_compose_list(void)
2383 return compose_list;
2386 void compose_entry_append(Compose *compose, const gchar *address,
2387 ComposeEntryType type)
2389 const gchar *header;
2391 gboolean in_quote = FALSE;
2392 if (!address || *address == '\0') return;
2399 header = N_("Bcc:");
2401 case COMPOSE_REPLYTO:
2402 header = N_("Reply-To:");
2404 case COMPOSE_NEWSGROUPS:
2405 header = N_("Newsgroups:");
2407 case COMPOSE_FOLLOWUPTO:
2408 header = N_( "Followup-To:");
2415 header = prefs_common_translated_header_name(header);
2417 cur = begin = (gchar *)address;
2419 /* we separate the line by commas, but not if we're inside a quoted
2421 while (*cur != '\0') {
2423 in_quote = !in_quote;
2424 if (*cur == ',' && !in_quote) {
2425 gchar *tmp = g_strdup(begin);
2427 tmp[cur-begin]='\0';
2430 while (*tmp == ' ' || *tmp == '\t')
2432 compose_add_header_entry(compose, header, tmp);
2439 gchar *tmp = g_strdup(begin);
2441 tmp[cur-begin]='\0';
2444 while (*tmp == ' ' || *tmp == '\t')
2446 compose_add_header_entry(compose, header, tmp);
2451 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2453 static GdkColor yellow;
2454 static GdkColor black;
2455 static gboolean yellow_initialised = FALSE;
2459 if (!yellow_initialised) {
2460 gdk_color_parse("#f5f6be", &yellow);
2461 gdk_color_parse("#000000", &black);
2462 yellow_initialised = gdk_colormap_alloc_color(
2463 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2464 yellow_initialised &= gdk_colormap_alloc_color(
2465 gdk_colormap_get_system(), &black, FALSE, TRUE);
2468 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2469 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2470 if (gtk_entry_get_text(entry) &&
2471 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2472 if (yellow_initialised) {
2473 gtk_widget_modify_base(
2474 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2475 GTK_STATE_NORMAL, &yellow);
2476 gtk_widget_modify_text(
2477 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2478 GTK_STATE_NORMAL, &black);
2484 void compose_toolbar_cb(gint action, gpointer data)
2486 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2487 Compose *compose = (Compose*)toolbar_item->parent;
2489 cm_return_if_fail(compose != NULL);
2493 compose_send_cb(NULL, compose);
2496 compose_send_later_cb(NULL, compose);
2499 compose_draft(compose, COMPOSE_QUIT_EDITING);
2502 compose_insert_file_cb(NULL, compose);
2505 compose_attach_cb(NULL, compose);
2508 compose_insert_sig(compose, FALSE);
2511 compose_ext_editor_cb(NULL, compose);
2513 case A_LINEWRAP_CURRENT:
2514 compose_beautify_paragraph(compose, NULL, TRUE);
2516 case A_LINEWRAP_ALL:
2517 compose_wrap_all_full(compose, TRUE);
2520 compose_address_cb(NULL, compose);
2523 case A_CHECK_SPELLING:
2524 compose_check_all(NULL, compose);
2532 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2537 gchar *subject = NULL;
2541 gchar **attach = NULL;
2543 /* get mailto parts but skip from */
2544 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2547 compose_entry_append(compose, to, to_type);
2549 compose_entry_append(compose, cc, COMPOSE_CC);
2551 compose_entry_append(compose, bcc, COMPOSE_BCC);
2553 if (!g_utf8_validate (subject, -1, NULL)) {
2554 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2555 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2558 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2562 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2563 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2566 gboolean prev_autowrap = compose->autowrap;
2568 compose->autowrap = FALSE;
2570 mark = gtk_text_buffer_get_insert(buffer);
2571 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2573 if (!g_utf8_validate (body, -1, NULL)) {
2574 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2575 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2578 gtk_text_buffer_insert(buffer, &iter, body, -1);
2580 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2582 compose->autowrap = prev_autowrap;
2583 if (compose->autowrap)
2584 compose_wrap_all(compose);
2588 gint i = 0, att = 0;
2589 gchar *warn_files = NULL;
2590 while (attach[i] != NULL) {
2591 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2592 if (utf8_filename) {
2593 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2594 gchar *tmp = g_strdup_printf("%s%s\n",
2595 warn_files?warn_files:"",
2601 g_free(utf8_filename);
2603 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2608 alertpanel_notice(ngettext(
2609 "The following file has been attached: \n%s",
2610 "The following files have been attached: \n%s", att), warn_files);
2622 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2624 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2625 {"Cc:", NULL, TRUE},
2626 {"References:", NULL, FALSE},
2627 {"Bcc:", NULL, TRUE},
2628 {"Newsgroups:", NULL, TRUE},
2629 {"Followup-To:", NULL, TRUE},
2630 {"List-Post:", NULL, FALSE},
2631 {"X-Priority:", NULL, FALSE},
2632 {NULL, NULL, FALSE}};
2648 cm_return_val_if_fail(msginfo != NULL, -1);
2650 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2651 procheader_get_header_fields(fp, hentry);
2654 if (hentry[H_REPLY_TO].body != NULL) {
2655 if (hentry[H_REPLY_TO].body[0] != '\0') {
2657 conv_unmime_header(hentry[H_REPLY_TO].body,
2660 g_free(hentry[H_REPLY_TO].body);
2661 hentry[H_REPLY_TO].body = NULL;
2663 if (hentry[H_CC].body != NULL) {
2664 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2665 g_free(hentry[H_CC].body);
2666 hentry[H_CC].body = NULL;
2668 if (hentry[H_REFERENCES].body != NULL) {
2669 if (compose->mode == COMPOSE_REEDIT)
2670 compose->references = hentry[H_REFERENCES].body;
2672 compose->references = compose_parse_references
2673 (hentry[H_REFERENCES].body, msginfo->msgid);
2674 g_free(hentry[H_REFERENCES].body);
2676 hentry[H_REFERENCES].body = NULL;
2678 if (hentry[H_BCC].body != NULL) {
2679 if (compose->mode == COMPOSE_REEDIT)
2681 conv_unmime_header(hentry[H_BCC].body, NULL);
2682 g_free(hentry[H_BCC].body);
2683 hentry[H_BCC].body = NULL;
2685 if (hentry[H_NEWSGROUPS].body != NULL) {
2686 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2687 hentry[H_NEWSGROUPS].body = NULL;
2689 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2690 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2691 compose->followup_to =
2692 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2695 g_free(hentry[H_FOLLOWUP_TO].body);
2696 hentry[H_FOLLOWUP_TO].body = NULL;
2698 if (hentry[H_LIST_POST].body != NULL) {
2701 extract_address(hentry[H_LIST_POST].body);
2702 if (hentry[H_LIST_POST].body[0] != '\0') {
2703 scan_mailto_url(hentry[H_LIST_POST].body,
2704 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2706 g_free(compose->ml_post);
2707 compose->ml_post = to;
2710 g_free(hentry[H_LIST_POST].body);
2711 hentry[H_LIST_POST].body = NULL;
2714 /* CLAWS - X-Priority */
2715 if (compose->mode == COMPOSE_REEDIT)
2716 if (hentry[H_X_PRIORITY].body != NULL) {
2719 priority = atoi(hentry[H_X_PRIORITY].body);
2720 g_free(hentry[H_X_PRIORITY].body);
2722 hentry[H_X_PRIORITY].body = NULL;
2724 if (priority < PRIORITY_HIGHEST ||
2725 priority > PRIORITY_LOWEST)
2726 priority = PRIORITY_NORMAL;
2728 compose->priority = priority;
2731 if (compose->mode == COMPOSE_REEDIT) {
2732 if (msginfo->inreplyto && *msginfo->inreplyto)
2733 compose->inreplyto = g_strdup(msginfo->inreplyto);
2737 if (msginfo->msgid && *msginfo->msgid)
2738 compose->inreplyto = g_strdup(msginfo->msgid);
2740 if (!compose->references) {
2741 if (msginfo->msgid && *msginfo->msgid) {
2742 if (msginfo->inreplyto && *msginfo->inreplyto)
2743 compose->references =
2744 g_strdup_printf("<%s>\n\t<%s>",
2748 compose->references =
2749 g_strconcat("<", msginfo->msgid, ">",
2751 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2752 compose->references =
2753 g_strconcat("<", msginfo->inreplyto, ">",
2761 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2763 GSList *ref_id_list, *cur;
2767 ref_id_list = references_list_append(NULL, ref);
2768 if (!ref_id_list) return NULL;
2769 if (msgid && *msgid)
2770 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2775 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2776 /* "<" + Message-ID + ">" + CR+LF+TAB */
2777 len += strlen((gchar *)cur->data) + 5;
2779 if (len > MAX_REFERENCES_LEN) {
2780 /* remove second message-ID */
2781 if (ref_id_list && ref_id_list->next &&
2782 ref_id_list->next->next) {
2783 g_free(ref_id_list->next->data);
2784 ref_id_list = g_slist_remove
2785 (ref_id_list, ref_id_list->next->data);
2787 slist_free_strings(ref_id_list);
2788 g_slist_free(ref_id_list);
2795 new_ref = g_string_new("");
2796 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2797 if (new_ref->len > 0)
2798 g_string_append(new_ref, "\n\t");
2799 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2802 slist_free_strings(ref_id_list);
2803 g_slist_free(ref_id_list);
2805 new_ref_str = new_ref->str;
2806 g_string_free(new_ref, FALSE);
2811 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2812 const gchar *fmt, const gchar *qmark,
2813 const gchar *body, gboolean rewrap,
2814 gboolean need_unescape,
2815 const gchar *err_msg)
2817 MsgInfo* dummyinfo = NULL;
2818 gchar *quote_str = NULL;
2820 gboolean prev_autowrap;
2821 const gchar *trimmed_body = body;
2822 gint cursor_pos = -1;
2823 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2824 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2829 SIGNAL_BLOCK(buffer);
2832 dummyinfo = compose_msginfo_new_from_compose(compose);
2833 msginfo = dummyinfo;
2836 if (qmark != NULL) {
2838 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2839 compose->gtkaspell);
2841 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2843 quote_fmt_scan_string(qmark);
2846 buf = quote_fmt_get_buffer();
2848 alertpanel_error(_("Quote mark format error."));
2850 Xstrdup_a(quote_str, buf, goto error)
2853 if (fmt && *fmt != '\0') {
2856 while (*trimmed_body == '\n')
2860 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2861 compose->gtkaspell);
2863 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2865 if (need_unescape) {
2868 /* decode \-escape sequences in the internal representation of the quote format */
2869 tmp = malloc(strlen(fmt)+1);
2870 pref_get_unescaped_pref(tmp, fmt);
2871 quote_fmt_scan_string(tmp);
2875 quote_fmt_scan_string(fmt);
2879 buf = quote_fmt_get_buffer();
2881 gint line = quote_fmt_get_line();
2882 alertpanel_error(err_msg, line);
2888 prev_autowrap = compose->autowrap;
2889 compose->autowrap = FALSE;
2891 mark = gtk_text_buffer_get_insert(buffer);
2892 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2893 if (g_utf8_validate(buf, -1, NULL)) {
2894 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2896 gchar *tmpout = NULL;
2897 tmpout = conv_codeset_strdup
2898 (buf, conv_get_locale_charset_str_no_utf8(),
2900 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2902 tmpout = g_malloc(strlen(buf)*2+1);
2903 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2905 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2909 cursor_pos = quote_fmt_get_cursor_pos();
2910 if (cursor_pos == -1)
2911 cursor_pos = gtk_text_iter_get_offset(&iter);
2912 compose->set_cursor_pos = cursor_pos;
2914 gtk_text_buffer_get_start_iter(buffer, &iter);
2915 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2916 gtk_text_buffer_place_cursor(buffer, &iter);
2918 compose->autowrap = prev_autowrap;
2919 if (compose->autowrap && rewrap)
2920 compose_wrap_all(compose);
2927 SIGNAL_UNBLOCK(buffer);
2929 procmsg_msginfo_free( dummyinfo );
2934 /* if ml_post is of type addr@host and from is of type
2935 * addr-anything@host, return TRUE
2937 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2939 gchar *left_ml = NULL;
2940 gchar *right_ml = NULL;
2941 gchar *left_from = NULL;
2942 gchar *right_from = NULL;
2943 gboolean result = FALSE;
2945 if (!ml_post || !from)
2948 left_ml = g_strdup(ml_post);
2949 if (strstr(left_ml, "@")) {
2950 right_ml = strstr(left_ml, "@")+1;
2951 *(strstr(left_ml, "@")) = '\0';
2954 left_from = g_strdup(from);
2955 if (strstr(left_from, "@")) {
2956 right_from = strstr(left_from, "@")+1;
2957 *(strstr(left_from, "@")) = '\0';
2960 if (left_ml && left_from && right_ml && right_from
2961 && !strncmp(left_from, left_ml, strlen(left_ml))
2962 && !strcmp(right_from, right_ml)) {
2971 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2973 gchar *my_addr1, *my_addr2;
2975 if (!addr1 || !addr2)
2978 Xstrdup_a(my_addr1, addr1, return FALSE);
2979 Xstrdup_a(my_addr2, addr2, return FALSE);
2981 extract_address(my_addr1);
2982 extract_address(my_addr2);
2984 return !strcasecmp(my_addr1, my_addr2);
2987 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2988 gboolean to_all, gboolean to_ml,
2990 gboolean followup_and_reply_to)
2992 GSList *cc_list = NULL;
2995 gchar *replyto = NULL;
2996 GHashTable *to_table;
2998 gboolean reply_to_ml = FALSE;
2999 gboolean default_reply_to = FALSE;
3001 cm_return_if_fail(compose->account != NULL);
3002 cm_return_if_fail(msginfo != NULL);
3004 reply_to_ml = to_ml && compose->ml_post;
3006 default_reply_to = msginfo->folder &&
3007 msginfo->folder->prefs->enable_default_reply_to;
3009 if (compose->account->protocol != A_NNTP) {
3010 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3011 if (msginfo->folder->prefs->enable_default_replyto) {
3012 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3014 if (msginfo->folder->prefs->enable_default_bcc) {
3015 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3017 if (msginfo->folder->prefs->enable_default_cc) {
3018 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3021 if (reply_to_ml && !default_reply_to) {
3023 gboolean is_subscr = is_subscription(compose->ml_post,
3026 /* normal answer to ml post with a reply-to */
3027 compose_entry_append(compose,
3030 if (compose->replyto
3031 && !same_address(compose->ml_post, compose->replyto))
3032 compose_entry_append(compose,
3036 /* answer to subscription confirmation */
3037 if (compose->replyto)
3038 compose_entry_append(compose,
3041 else if (msginfo->from)
3042 compose_entry_append(compose,
3047 else if (!(to_all || to_sender) && default_reply_to) {
3048 compose_entry_append(compose,
3049 msginfo->folder->prefs->default_reply_to,
3051 compose_entry_mark_default_to(compose,
3052 msginfo->folder->prefs->default_reply_to);
3057 Xstrdup_a(tmp1, msginfo->from, return);
3058 extract_address(tmp1);
3059 if (to_all || to_sender ||
3060 !account_find_from_address(tmp1, FALSE))
3061 compose_entry_append(compose,
3062 (compose->replyto && !to_sender)
3063 ? compose->replyto :
3064 msginfo->from ? msginfo->from : "",
3066 else if (!to_all && !to_sender) {
3067 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3068 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3069 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3070 if (compose->replyto) {
3071 compose_entry_append(compose,
3075 compose_entry_append(compose,
3076 msginfo->from ? msginfo->from : "",
3080 /* replying to own mail, use original recp */
3081 compose_entry_append(compose,
3082 msginfo->to ? msginfo->to : "",
3084 compose_entry_append(compose,
3085 msginfo->cc ? msginfo->cc : "",
3091 if (to_sender || (compose->followup_to &&
3092 !strncmp(compose->followup_to, "poster", 6)))
3093 compose_entry_append
3095 (compose->replyto ? compose->replyto :
3096 msginfo->from ? msginfo->from : ""),
3099 else if (followup_and_reply_to || to_all) {
3100 compose_entry_append
3102 (compose->replyto ? compose->replyto :
3103 msginfo->from ? msginfo->from : ""),
3106 compose_entry_append
3108 compose->followup_to ? compose->followup_to :
3109 compose->newsgroups ? compose->newsgroups : "",
3110 COMPOSE_NEWSGROUPS);
3113 compose_entry_append
3115 compose->followup_to ? compose->followup_to :
3116 compose->newsgroups ? compose->newsgroups : "",
3117 COMPOSE_NEWSGROUPS);
3120 if (msginfo->subject && *msginfo->subject) {
3124 buf = p = g_strdup(msginfo->subject);
3125 p += subject_get_prefix_length(p);
3126 memmove(buf, p, strlen(p) + 1);
3128 buf2 = g_strdup_printf("Re: %s", buf);
3129 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3134 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3136 if (to_ml && compose->ml_post) return;
3137 if (!to_all || compose->account->protocol == A_NNTP) return;
3139 if (compose->replyto) {
3140 Xstrdup_a(replyto, compose->replyto, return);
3141 extract_address(replyto);
3143 if (msginfo->from) {
3144 Xstrdup_a(from, msginfo->from, return);
3145 extract_address(from);
3148 if (replyto && from)
3149 cc_list = address_list_append_with_comments(cc_list, from);
3150 if (to_all && msginfo->folder &&
3151 msginfo->folder->prefs->enable_default_reply_to)
3152 cc_list = address_list_append_with_comments(cc_list,
3153 msginfo->folder->prefs->default_reply_to);
3154 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3155 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3157 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3159 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3160 if (compose->account) {
3161 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3162 GINT_TO_POINTER(1));
3164 /* remove address on To: and that of current account */
3165 for (cur = cc_list; cur != NULL; ) {
3166 GSList *next = cur->next;
3169 addr = g_utf8_strdown(cur->data, -1);
3170 extract_address(addr);
3172 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3173 cc_list = g_slist_remove(cc_list, cur->data);
3175 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3179 hash_free_strings(to_table);
3180 g_hash_table_destroy(to_table);
3183 for (cur = cc_list; cur != NULL; cur = cur->next)
3184 compose_entry_append(compose, (gchar *)cur->data,
3186 slist_free_strings(cc_list);
3187 g_slist_free(cc_list);
3192 #define SET_ENTRY(entry, str) \
3195 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3198 #define SET_ADDRESS(type, str) \
3201 compose_entry_append(compose, str, type); \
3204 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3206 cm_return_if_fail(msginfo != NULL);
3208 SET_ENTRY(subject_entry, msginfo->subject);
3209 SET_ENTRY(from_name, msginfo->from);
3210 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3211 SET_ADDRESS(COMPOSE_CC, compose->cc);
3212 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3213 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3214 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3215 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3217 compose_update_priority_menu_item(compose);
3218 compose_update_privacy_system_menu_item(compose, FALSE);
3219 compose_show_first_last_header(compose, TRUE);
3225 static void compose_insert_sig(Compose *compose, gboolean replace)
3227 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3228 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3230 GtkTextIter iter, iter_end;
3231 gint cur_pos, ins_pos;
3232 gboolean prev_autowrap;
3233 gboolean found = FALSE;
3234 gboolean exists = FALSE;
3236 cm_return_if_fail(compose->account != NULL);
3240 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3241 G_CALLBACK(compose_changed_cb),
3244 mark = gtk_text_buffer_get_insert(buffer);
3245 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3246 cur_pos = gtk_text_iter_get_offset (&iter);
3249 gtk_text_buffer_get_end_iter(buffer, &iter);
3251 exists = (compose->sig_str != NULL);
3254 GtkTextIter first_iter, start_iter, end_iter;
3256 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3258 if (!exists || compose->sig_str[0] == '\0')
3261 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3262 compose->signature_tag);
3265 /* include previous \n\n */
3266 gtk_text_iter_backward_chars(&first_iter, 1);
3267 start_iter = first_iter;
3268 end_iter = first_iter;
3270 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3271 compose->signature_tag);
3272 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3273 compose->signature_tag);
3275 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3281 g_free(compose->sig_str);
3282 compose->sig_str = account_get_signature_str(compose->account);
3284 cur_pos = gtk_text_iter_get_offset(&iter);
3286 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3287 g_free(compose->sig_str);
3288 compose->sig_str = NULL;
3290 if (compose->sig_inserted == FALSE)
3291 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3292 compose->sig_inserted = TRUE;
3294 cur_pos = gtk_text_iter_get_offset(&iter);
3295 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3297 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3298 gtk_text_iter_forward_chars(&iter, 1);
3299 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3300 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3302 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3303 cur_pos = gtk_text_buffer_get_char_count (buffer);
3306 /* put the cursor where it should be
3307 * either where the quote_fmt says, either where it was */
3308 if (compose->set_cursor_pos < 0)
3309 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3311 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3312 compose->set_cursor_pos);
3314 compose->set_cursor_pos = -1;
3315 gtk_text_buffer_place_cursor(buffer, &iter);
3316 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3317 G_CALLBACK(compose_changed_cb),
3323 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3326 GtkTextBuffer *buffer;
3329 const gchar *cur_encoding;
3330 gchar buf[BUFFSIZE];
3333 gboolean prev_autowrap;
3334 gboolean badtxt = FALSE;
3336 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3338 if ((fp = g_fopen(file, "rb")) == NULL) {
3339 FILE_OP_ERROR(file, "fopen");
3340 return COMPOSE_INSERT_READ_ERROR;
3343 prev_autowrap = compose->autowrap;
3344 compose->autowrap = FALSE;
3346 text = GTK_TEXT_VIEW(compose->text);
3347 buffer = gtk_text_view_get_buffer(text);
3348 mark = gtk_text_buffer_get_insert(buffer);
3349 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3351 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3352 G_CALLBACK(text_inserted),
3355 cur_encoding = conv_get_locale_charset_str_no_utf8();
3357 while (fgets(buf, sizeof(buf), fp) != NULL) {
3360 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3361 str = g_strdup(buf);
3363 str = conv_codeset_strdup
3364 (buf, cur_encoding, CS_INTERNAL);
3367 /* strip <CR> if DOS/Windows file,
3368 replace <CR> with <LF> if Macintosh file. */
3371 if (len > 0 && str[len - 1] != '\n') {
3373 if (str[len] == '\r') str[len] = '\n';
3376 gtk_text_buffer_insert(buffer, &iter, str, -1);
3380 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3381 G_CALLBACK(text_inserted),
3383 compose->autowrap = prev_autowrap;
3384 if (compose->autowrap)
3385 compose_wrap_all(compose);
3390 return COMPOSE_INSERT_INVALID_CHARACTER;
3392 return COMPOSE_INSERT_SUCCESS;
3395 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3396 const gchar *filename,
3397 const gchar *content_type)
3405 GtkListStore *store;
3407 gboolean has_binary = FALSE;
3409 if (!is_file_exist(file)) {
3410 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3411 gboolean result = FALSE;
3412 if (file_from_uri && is_file_exist(file_from_uri)) {
3413 result = compose_attach_append(
3414 compose, file_from_uri,
3418 g_free(file_from_uri);
3421 alertpanel_error("File %s doesn't exist\n", filename);
3424 if ((size = get_file_size(file)) < 0) {
3425 alertpanel_error("Can't get file size of %s\n", filename);
3429 alertpanel_error(_("File %s is empty."), filename);
3432 if ((fp = g_fopen(file, "rb")) == NULL) {
3433 alertpanel_error(_("Can't read %s."), filename);
3438 ainfo = g_new0(AttachInfo, 1);
3439 auto_ainfo = g_auto_pointer_new_with_free
3440 (ainfo, (GFreeFunc) compose_attach_info_free);
3441 ainfo->file = g_strdup(file);
3444 ainfo->content_type = g_strdup(content_type);
3445 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3447 MsgFlags flags = {0, 0};
3449 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3450 ainfo->encoding = ENC_7BIT;
3452 ainfo->encoding = ENC_8BIT;
3454 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3455 if (msginfo && msginfo->subject)
3456 name = g_strdup(msginfo->subject);
3458 name = g_path_get_basename(filename ? filename : file);
3460 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3462 procmsg_msginfo_free(msginfo);
3464 if (!g_ascii_strncasecmp(content_type, "text", 4))
3465 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3467 ainfo->encoding = ENC_BASE64;
3468 name = g_path_get_basename(filename ? filename : file);
3469 ainfo->name = g_strdup(name);
3473 ainfo->content_type = procmime_get_mime_type(file);
3474 if (!ainfo->content_type) {
3475 ainfo->content_type =
3476 g_strdup("application/octet-stream");
3477 ainfo->encoding = ENC_BASE64;
3478 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3480 procmime_get_encoding_for_text_file(file, &has_binary);
3482 ainfo->encoding = ENC_BASE64;
3483 name = g_path_get_basename(filename ? filename : file);
3484 ainfo->name = g_strdup(name);
3488 if (ainfo->name != NULL
3489 && !strcmp(ainfo->name, ".")) {
3490 g_free(ainfo->name);
3494 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3495 g_free(ainfo->content_type);
3496 ainfo->content_type = g_strdup("application/octet-stream");
3499 ainfo->size = (goffset)size;
3500 size_text = to_human_readable((goffset)size);
3502 store = GTK_LIST_STORE(gtk_tree_view_get_model
3503 (GTK_TREE_VIEW(compose->attach_clist)));
3505 gtk_list_store_append(store, &iter);
3506 gtk_list_store_set(store, &iter,
3507 COL_MIMETYPE, ainfo->content_type,
3508 COL_SIZE, size_text,
3509 COL_NAME, ainfo->name,
3511 COL_AUTODATA, auto_ainfo,
3514 g_auto_pointer_free(auto_ainfo);
3515 compose_attach_update_label(compose);
3519 static void compose_use_signing(Compose *compose, gboolean use_signing)
3521 compose->use_signing = use_signing;
3522 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3525 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3527 compose->use_encryption = use_encryption;
3528 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3531 #define NEXT_PART_NOT_CHILD(info) \
3533 node = info->node; \
3534 while (node->children) \
3535 node = g_node_last_child(node); \
3536 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3539 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3543 MimeInfo *firsttext = NULL;
3544 MimeInfo *encrypted = NULL;
3547 const gchar *partname = NULL;
3549 mimeinfo = procmime_scan_message(msginfo);
3550 if (!mimeinfo) return;
3552 if (mimeinfo->node->children == NULL) {
3553 procmime_mimeinfo_free_all(mimeinfo);
3557 /* find first content part */
3558 child = (MimeInfo *) mimeinfo->node->children->data;
3559 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3560 child = (MimeInfo *)child->node->children->data;
3563 if (child->type == MIMETYPE_TEXT) {
3565 debug_print("First text part found\n");
3566 } else if (compose->mode == COMPOSE_REEDIT &&
3567 child->type == MIMETYPE_APPLICATION &&
3568 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3569 encrypted = (MimeInfo *)child->node->parent->data;
3572 child = (MimeInfo *) mimeinfo->node->children->data;
3573 while (child != NULL) {
3576 if (child == encrypted) {
3577 /* skip this part of tree */
3578 NEXT_PART_NOT_CHILD(child);
3582 if (child->type == MIMETYPE_MULTIPART) {
3583 /* get the actual content */
3584 child = procmime_mimeinfo_next(child);
3588 if (child == firsttext) {
3589 child = procmime_mimeinfo_next(child);
3593 outfile = procmime_get_tmp_file_name(child);
3594 if ((err = procmime_get_part(outfile, child)) < 0)
3595 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3597 gchar *content_type;
3599 content_type = procmime_get_content_type_str(child->type, child->subtype);
3601 /* if we meet a pgp signature, we don't attach it, but
3602 * we force signing. */
3603 if ((strcmp(content_type, "application/pgp-signature") &&
3604 strcmp(content_type, "application/pkcs7-signature") &&
3605 strcmp(content_type, "application/x-pkcs7-signature"))
3606 || compose->mode == COMPOSE_REDIRECT) {
3607 partname = procmime_mimeinfo_get_parameter(child, "filename");
3608 if (partname == NULL)
3609 partname = procmime_mimeinfo_get_parameter(child, "name");
3610 if (partname == NULL)
3612 compose_attach_append(compose, outfile,
3613 partname, content_type);
3615 compose_force_signing(compose, compose->account, NULL);
3617 g_free(content_type);
3620 NEXT_PART_NOT_CHILD(child);
3622 procmime_mimeinfo_free_all(mimeinfo);
3625 #undef NEXT_PART_NOT_CHILD
3630 WAIT_FOR_INDENT_CHAR,
3631 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3634 /* return indent length, we allow:
3635 indent characters followed by indent characters or spaces/tabs,
3636 alphabets and numbers immediately followed by indent characters,
3637 and the repeating sequences of the above
3638 If quote ends with multiple spaces, only the first one is included. */
3639 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3640 const GtkTextIter *start, gint *len)
3642 GtkTextIter iter = *start;
3646 IndentState state = WAIT_FOR_INDENT_CHAR;
3649 gint alnum_count = 0;
3650 gint space_count = 0;
3653 if (prefs_common.quote_chars == NULL) {
3657 while (!gtk_text_iter_ends_line(&iter)) {
3658 wc = gtk_text_iter_get_char(&iter);
3659 if (g_unichar_iswide(wc))
3661 clen = g_unichar_to_utf8(wc, ch);
3665 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3666 is_space = g_unichar_isspace(wc);
3668 if (state == WAIT_FOR_INDENT_CHAR) {
3669 if (!is_indent && !g_unichar_isalnum(wc))
3672 quote_len += alnum_count + space_count + 1;
3673 alnum_count = space_count = 0;
3674 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3677 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3678 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3682 else if (is_indent) {
3683 quote_len += alnum_count + space_count + 1;
3684 alnum_count = space_count = 0;
3687 state = WAIT_FOR_INDENT_CHAR;
3691 gtk_text_iter_forward_char(&iter);
3694 if (quote_len > 0 && space_count > 0)
3700 if (quote_len > 0) {
3702 gtk_text_iter_forward_chars(&iter, quote_len);
3703 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3709 /* return >0 if the line is itemized */
3710 static int compose_itemized_length(GtkTextBuffer *buffer,
3711 const GtkTextIter *start)
3713 GtkTextIter iter = *start;
3718 if (gtk_text_iter_ends_line(&iter))
3723 wc = gtk_text_iter_get_char(&iter);
3724 if (!g_unichar_isspace(wc))
3726 gtk_text_iter_forward_char(&iter);
3727 if (gtk_text_iter_ends_line(&iter))
3731 clen = g_unichar_to_utf8(wc, ch);
3735 if (!strchr("*-+", ch[0]))
3738 gtk_text_iter_forward_char(&iter);
3739 if (gtk_text_iter_ends_line(&iter))
3741 wc = gtk_text_iter_get_char(&iter);
3742 if (g_unichar_isspace(wc)) {
3748 /* return the string at the start of the itemization */
3749 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3750 const GtkTextIter *start)
3752 GtkTextIter iter = *start;
3755 GString *item_chars = g_string_new("");
3758 if (gtk_text_iter_ends_line(&iter))
3763 wc = gtk_text_iter_get_char(&iter);
3764 if (!g_unichar_isspace(wc))
3766 gtk_text_iter_forward_char(&iter);
3767 if (gtk_text_iter_ends_line(&iter))
3769 g_string_append_unichar(item_chars, wc);
3772 str = item_chars->str;
3773 g_string_free(item_chars, FALSE);
3777 /* return the number of spaces at a line's start */
3778 static int compose_left_offset_length(GtkTextBuffer *buffer,
3779 const GtkTextIter *start)
3781 GtkTextIter iter = *start;
3784 if (gtk_text_iter_ends_line(&iter))
3788 wc = gtk_text_iter_get_char(&iter);
3789 if (!g_unichar_isspace(wc))
3792 gtk_text_iter_forward_char(&iter);
3793 if (gtk_text_iter_ends_line(&iter))
3797 gtk_text_iter_forward_char(&iter);
3798 if (gtk_text_iter_ends_line(&iter))
3803 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3804 const GtkTextIter *start,
3805 GtkTextIter *break_pos,
3809 GtkTextIter iter = *start, line_end = *start;
3810 PangoLogAttr *attrs;
3817 gboolean can_break = FALSE;
3818 gboolean do_break = FALSE;
3819 gboolean was_white = FALSE;
3820 gboolean prev_dont_break = FALSE;
3822 gtk_text_iter_forward_to_line_end(&line_end);
3823 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3824 len = g_utf8_strlen(str, -1);
3828 g_warning("compose_get_line_break_pos: len = 0!\n");
3832 /* g_print("breaking line: %d: %s (len = %d)\n",
3833 gtk_text_iter_get_line(&iter), str, len); */
3835 attrs = g_new(PangoLogAttr, len + 1);
3837 pango_default_break(str, -1, NULL, attrs, len + 1);
3841 /* skip quote and leading spaces */
3842 for (i = 0; *p != '\0' && i < len; i++) {
3845 wc = g_utf8_get_char(p);
3846 if (i >= quote_len && !g_unichar_isspace(wc))
3848 if (g_unichar_iswide(wc))
3850 else if (*p == '\t')
3854 p = g_utf8_next_char(p);
3857 for (; *p != '\0' && i < len; i++) {
3858 PangoLogAttr *attr = attrs + i;
3862 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3865 was_white = attr->is_white;
3867 /* don't wrap URI */
3868 if ((uri_len = get_uri_len(p)) > 0) {
3870 if (pos > 0 && col > max_col) {
3880 wc = g_utf8_get_char(p);
3881 if (g_unichar_iswide(wc)) {
3883 if (prev_dont_break && can_break && attr->is_line_break)
3885 } else if (*p == '\t')
3889 if (pos > 0 && col > max_col) {
3894 if (*p == '-' || *p == '/')
3895 prev_dont_break = TRUE;
3897 prev_dont_break = FALSE;
3899 p = g_utf8_next_char(p);
3903 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3908 *break_pos = *start;
3909 gtk_text_iter_set_line_offset(break_pos, pos);
3914 static gboolean compose_join_next_line(Compose *compose,
3915 GtkTextBuffer *buffer,
3917 const gchar *quote_str)
3919 GtkTextIter iter_ = *iter, cur, prev, next, end;
3920 PangoLogAttr attrs[3];
3922 gchar *next_quote_str;
3925 gboolean keep_cursor = FALSE;
3927 if (!gtk_text_iter_forward_line(&iter_) ||
3928 gtk_text_iter_ends_line(&iter_)) {
3931 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3933 if ((quote_str || next_quote_str) &&
3934 strcmp2(quote_str, next_quote_str) != 0) {
3935 g_free(next_quote_str);
3938 g_free(next_quote_str);
3941 if (quote_len > 0) {
3942 gtk_text_iter_forward_chars(&end, quote_len);
3943 if (gtk_text_iter_ends_line(&end)) {
3948 /* don't join itemized lines */
3949 if (compose_itemized_length(buffer, &end) > 0) {
3953 /* don't join signature separator */
3954 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3957 /* delete quote str */
3959 gtk_text_buffer_delete(buffer, &iter_, &end);
3961 /* don't join line breaks put by the user */
3963 gtk_text_iter_backward_char(&cur);
3964 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3965 gtk_text_iter_forward_char(&cur);
3969 gtk_text_iter_forward_char(&cur);
3970 /* delete linebreak and extra spaces */
3971 while (gtk_text_iter_backward_char(&cur)) {
3972 wc1 = gtk_text_iter_get_char(&cur);
3973 if (!g_unichar_isspace(wc1))
3978 while (!gtk_text_iter_ends_line(&cur)) {
3979 wc1 = gtk_text_iter_get_char(&cur);
3980 if (!g_unichar_isspace(wc1))
3982 gtk_text_iter_forward_char(&cur);
3985 if (!gtk_text_iter_equal(&prev, &next)) {
3988 mark = gtk_text_buffer_get_insert(buffer);
3989 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3990 if (gtk_text_iter_equal(&prev, &cur))
3992 gtk_text_buffer_delete(buffer, &prev, &next);
3996 /* insert space if required */
3997 gtk_text_iter_backward_char(&prev);
3998 wc1 = gtk_text_iter_get_char(&prev);
3999 wc2 = gtk_text_iter_get_char(&next);
4000 gtk_text_iter_forward_char(&next);
4001 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4002 pango_default_break(str, -1, NULL, attrs, 3);
4003 if (!attrs[1].is_line_break ||
4004 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4005 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4007 gtk_text_iter_backward_char(&iter_);
4008 gtk_text_buffer_place_cursor(buffer, &iter_);
4017 #define ADD_TXT_POS(bp_, ep_, pti_) \
4018 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4019 last = last->next; \
4020 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4021 last->next = NULL; \
4023 g_warning("alloc error scanning URIs\n"); \
4026 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4028 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4029 GtkTextBuffer *buffer;
4030 GtkTextIter iter, break_pos, end_of_line;
4031 gchar *quote_str = NULL;
4033 gboolean wrap_quote = prefs_common.linewrap_quote;
4034 gboolean prev_autowrap = compose->autowrap;
4035 gint startq_offset = -1, noq_offset = -1;
4036 gint uri_start = -1, uri_stop = -1;
4037 gint nouri_start = -1, nouri_stop = -1;
4038 gint num_blocks = 0;
4039 gint quotelevel = -1;
4040 gboolean modified = force;
4041 gboolean removed = FALSE;
4042 gboolean modified_before_remove = FALSE;
4044 gboolean start = TRUE;
4045 gint itemized_len = 0, rem_item_len = 0;
4046 gchar *itemized_chars = NULL;
4047 gboolean item_continuation = FALSE;
4052 if (compose->draft_timeout_tag == -2) {
4056 compose->autowrap = FALSE;
4058 buffer = gtk_text_view_get_buffer(text);
4059 undo_wrapping(compose->undostruct, TRUE);
4064 mark = gtk_text_buffer_get_insert(buffer);
4065 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4069 if (compose->draft_timeout_tag == -2) {
4070 if (gtk_text_iter_ends_line(&iter)) {
4071 while (gtk_text_iter_ends_line(&iter) &&
4072 gtk_text_iter_forward_line(&iter))
4075 while (gtk_text_iter_backward_line(&iter)) {
4076 if (gtk_text_iter_ends_line(&iter)) {
4077 gtk_text_iter_forward_line(&iter);
4083 /* move to line start */
4084 gtk_text_iter_set_line_offset(&iter, 0);
4087 itemized_len = compose_itemized_length(buffer, &iter);
4089 if (!itemized_len) {
4090 itemized_len = compose_left_offset_length(buffer, &iter);
4091 item_continuation = TRUE;
4095 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4097 /* go until paragraph end (empty line) */
4098 while (start || !gtk_text_iter_ends_line(&iter)) {
4099 gchar *scanpos = NULL;
4100 /* parse table - in order of priority */
4102 const gchar *needle; /* token */
4104 /* token search function */
4105 gchar *(*search) (const gchar *haystack,
4106 const gchar *needle);
4107 /* part parsing function */
4108 gboolean (*parse) (const gchar *start,
4109 const gchar *scanpos,
4113 /* part to URI function */
4114 gchar *(*build_uri) (const gchar *bp,
4118 static struct table parser[] = {
4119 {"http://", strcasestr, get_uri_part, make_uri_string},
4120 {"https://", strcasestr, get_uri_part, make_uri_string},
4121 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4122 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4123 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4124 {"www.", strcasestr, get_uri_part, make_http_string},
4125 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4126 {"@", strcasestr, get_email_part, make_email_string}
4128 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4129 gint last_index = PARSE_ELEMS;
4131 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4135 if (!prev_autowrap && num_blocks == 0) {
4137 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4138 G_CALLBACK(text_inserted),
4141 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4144 uri_start = uri_stop = -1;
4146 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4149 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4150 if (startq_offset == -1)
4151 startq_offset = gtk_text_iter_get_offset(&iter);
4152 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4153 if (quotelevel > 2) {
4154 /* recycle colors */
4155 if (prefs_common.recycle_quote_colors)
4164 if (startq_offset == -1)
4165 noq_offset = gtk_text_iter_get_offset(&iter);
4169 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4172 if (gtk_text_iter_ends_line(&iter)) {
4174 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4175 prefs_common.linewrap_len,
4177 GtkTextIter prev, next, cur;
4178 if (prev_autowrap != FALSE || force) {
4179 compose->automatic_break = TRUE;
4181 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4182 compose->automatic_break = FALSE;
4183 if (itemized_len && compose->autoindent) {
4184 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4185 if (!item_continuation)
4186 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4188 } else if (quote_str && wrap_quote) {
4189 compose->automatic_break = TRUE;
4191 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4192 compose->automatic_break = FALSE;
4193 if (itemized_len && compose->autoindent) {
4194 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4195 if (!item_continuation)
4196 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4200 /* remove trailing spaces */
4202 rem_item_len = itemized_len;
4203 while (compose->autoindent && rem_item_len-- > 0)
4204 gtk_text_iter_backward_char(&cur);
4205 gtk_text_iter_backward_char(&cur);
4208 while (!gtk_text_iter_starts_line(&cur)) {
4211 gtk_text_iter_backward_char(&cur);
4212 wc = gtk_text_iter_get_char(&cur);
4213 if (!g_unichar_isspace(wc))
4217 if (!gtk_text_iter_equal(&prev, &next)) {
4218 gtk_text_buffer_delete(buffer, &prev, &next);
4220 gtk_text_iter_forward_char(&break_pos);
4224 gtk_text_buffer_insert(buffer, &break_pos,
4228 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4230 /* move iter to current line start */
4231 gtk_text_iter_set_line_offset(&iter, 0);
4238 /* move iter to next line start */
4244 if (!prev_autowrap && num_blocks > 0) {
4246 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4247 G_CALLBACK(text_inserted),
4251 while (!gtk_text_iter_ends_line(&end_of_line)) {
4252 gtk_text_iter_forward_char(&end_of_line);
4254 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4256 nouri_start = gtk_text_iter_get_offset(&iter);
4257 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4259 walk_pos = gtk_text_iter_get_offset(&iter);
4260 /* FIXME: this looks phony. scanning for anything in the parse table */
4261 for (n = 0; n < PARSE_ELEMS; n++) {
4264 tmp = parser[n].search(walk, parser[n].needle);
4266 if (scanpos == NULL || tmp < scanpos) {
4275 /* check if URI can be parsed */
4276 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4277 (const gchar **)&ep, FALSE)
4278 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4282 strlen(parser[last_index].needle);
4285 uri_start = walk_pos + (bp - o_walk);
4286 uri_stop = walk_pos + (ep - o_walk);
4290 gtk_text_iter_forward_line(&iter);
4293 if (startq_offset != -1) {
4294 GtkTextIter startquote, endquote;
4295 gtk_text_buffer_get_iter_at_offset(
4296 buffer, &startquote, startq_offset);
4299 switch (quotelevel) {
4301 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4302 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4303 gtk_text_buffer_apply_tag_by_name(
4304 buffer, "quote0", &startquote, &endquote);
4305 gtk_text_buffer_remove_tag_by_name(
4306 buffer, "quote1", &startquote, &endquote);
4307 gtk_text_buffer_remove_tag_by_name(
4308 buffer, "quote2", &startquote, &endquote);
4313 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4314 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4315 gtk_text_buffer_apply_tag_by_name(
4316 buffer, "quote1", &startquote, &endquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote0", &startquote, &endquote);
4319 gtk_text_buffer_remove_tag_by_name(
4320 buffer, "quote2", &startquote, &endquote);
4325 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4326 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4327 gtk_text_buffer_apply_tag_by_name(
4328 buffer, "quote2", &startquote, &endquote);
4329 gtk_text_buffer_remove_tag_by_name(
4330 buffer, "quote0", &startquote, &endquote);
4331 gtk_text_buffer_remove_tag_by_name(
4332 buffer, "quote1", &startquote, &endquote);
4338 } else if (noq_offset != -1) {
4339 GtkTextIter startnoquote, endnoquote;
4340 gtk_text_buffer_get_iter_at_offset(
4341 buffer, &startnoquote, noq_offset);
4344 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4345 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4346 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4347 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4348 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4349 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4350 gtk_text_buffer_remove_tag_by_name(
4351 buffer, "quote0", &startnoquote, &endnoquote);
4352 gtk_text_buffer_remove_tag_by_name(
4353 buffer, "quote1", &startnoquote, &endnoquote);
4354 gtk_text_buffer_remove_tag_by_name(
4355 buffer, "quote2", &startnoquote, &endnoquote);
4361 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4362 GtkTextIter nouri_start_iter, nouri_end_iter;
4363 gtk_text_buffer_get_iter_at_offset(
4364 buffer, &nouri_start_iter, nouri_start);
4365 gtk_text_buffer_get_iter_at_offset(
4366 buffer, &nouri_end_iter, nouri_stop);
4367 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4368 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4369 gtk_text_buffer_remove_tag_by_name(
4370 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4371 modified_before_remove = modified;
4376 if (uri_start >= 0 && uri_stop > 0) {
4377 GtkTextIter uri_start_iter, uri_end_iter, back;
4378 gtk_text_buffer_get_iter_at_offset(
4379 buffer, &uri_start_iter, uri_start);
4380 gtk_text_buffer_get_iter_at_offset(
4381 buffer, &uri_end_iter, uri_stop);
4382 back = uri_end_iter;
4383 gtk_text_iter_backward_char(&back);
4384 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4385 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4386 gtk_text_buffer_apply_tag_by_name(
4387 buffer, "link", &uri_start_iter, &uri_end_iter);
4389 if (removed && !modified_before_remove) {
4395 // debug_print("not modified, out after %d lines\n", lines);
4399 // debug_print("modified, out after %d lines\n", lines);
4401 g_free(itemized_chars);
4404 undo_wrapping(compose->undostruct, FALSE);
4405 compose->autowrap = prev_autowrap;
4410 void compose_action_cb(void *data)
4412 Compose *compose = (Compose *)data;
4413 compose_wrap_all(compose);
4416 static void compose_wrap_all(Compose *compose)
4418 compose_wrap_all_full(compose, FALSE);
4421 static void compose_wrap_all_full(Compose *compose, gboolean force)
4423 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4424 GtkTextBuffer *buffer;
4426 gboolean modified = TRUE;
4428 buffer = gtk_text_view_get_buffer(text);
4430 gtk_text_buffer_get_start_iter(buffer, &iter);
4431 while (!gtk_text_iter_is_end(&iter) && modified)
4432 modified = compose_beautify_paragraph(compose, &iter, force);
4436 static void compose_set_title(Compose *compose)
4442 edited = compose->modified ? _(" [Edited]") : "";
4444 subject = gtk_editable_get_chars(
4445 GTK_EDITABLE(compose->subject_entry), 0, -1);
4447 #ifndef GENERIC_UMPC
4448 if (subject && strlen(subject))
4449 str = g_strdup_printf(_("%s - Compose message%s"),
4452 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4454 str = g_strdup(_("Compose message"));
4457 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4463 * compose_current_mail_account:
4465 * Find a current mail account (the currently selected account, or the
4466 * default account, if a news account is currently selected). If a
4467 * mail account cannot be found, display an error message.
4469 * Return value: Mail account, or NULL if not found.
4471 static PrefsAccount *
4472 compose_current_mail_account(void)
4476 if (cur_account && cur_account->protocol != A_NNTP)
4479 ac = account_get_default();
4480 if (!ac || ac->protocol == A_NNTP) {
4481 alertpanel_error(_("Account for sending mail is not specified.\n"
4482 "Please select a mail account before sending."));
4489 #define QUOTE_IF_REQUIRED(out, str) \
4491 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4495 len = strlen(str) + 3; \
4496 if ((__tmp = alloca(len)) == NULL) { \
4497 g_warning("can't allocate memory\n"); \
4498 g_string_free(header, TRUE); \
4501 g_snprintf(__tmp, len, "\"%s\"", str); \
4506 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4507 g_warning("can't allocate memory\n"); \
4508 g_string_free(header, TRUE); \
4511 strcpy(__tmp, str); \
4517 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4519 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4523 len = strlen(str) + 3; \
4524 if ((__tmp = alloca(len)) == NULL) { \
4525 g_warning("can't allocate memory\n"); \
4528 g_snprintf(__tmp, len, "\"%s\"", str); \
4533 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4534 g_warning("can't allocate memory\n"); \
4537 strcpy(__tmp, str); \
4543 static void compose_select_account(Compose *compose, PrefsAccount *account,
4546 gchar *from = NULL, *header;
4547 ComposeHeaderEntry *header_entry;
4549 cm_return_if_fail(account != NULL);
4551 compose->account = account;
4553 if (account->name && *account->name) {
4555 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4556 from = g_strdup_printf("%s <%s>",
4557 buf, account->address);
4558 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4560 from = g_strdup_printf("<%s>",
4562 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4567 compose_set_title(compose);
4569 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4572 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4573 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4574 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4576 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4578 activate_privacy_system(compose, account, FALSE);
4580 if (!init && compose->mode != COMPOSE_REDIRECT) {
4581 undo_block(compose->undostruct);
4582 compose_insert_sig(compose, TRUE);
4583 undo_unblock(compose->undostruct);
4586 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4587 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4589 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4590 if (account->protocol == A_NNTP) {
4591 if (!strcmp(header, _("To:")))
4592 combobox_select_by_text(
4593 GTK_COMBO_BOX(header_entry->combo),
4596 if (!strcmp(header, _("Newsgroups:")))
4597 combobox_select_by_text(
4598 GTK_COMBO_BOX(header_entry->combo),
4606 /* use account's dict info if set */
4607 if (compose->gtkaspell) {
4608 if (account->enable_default_dictionary)
4609 gtkaspell_change_dict(compose->gtkaspell,
4610 account->default_dictionary, FALSE);
4611 if (account->enable_default_alt_dictionary)
4612 gtkaspell_change_alt_dict(compose->gtkaspell,
4613 account->default_alt_dictionary);
4614 if (account->enable_default_dictionary
4615 || account->enable_default_alt_dictionary)
4616 compose_spell_menu_changed(compose);
4621 gboolean compose_check_for_valid_recipient(Compose *compose) {
4622 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4623 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4624 gboolean recipient_found = FALSE;
4628 /* free to and newsgroup list */
4629 slist_free_strings(compose->to_list);
4630 g_slist_free(compose->to_list);
4631 compose->to_list = NULL;
4633 slist_free_strings(compose->newsgroup_list);
4634 g_slist_free(compose->newsgroup_list);
4635 compose->newsgroup_list = NULL;
4637 /* search header entries for to and newsgroup entries */
4638 for (list = compose->header_list; list; list = list->next) {
4641 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4642 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4645 if (entry[0] != '\0') {
4646 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4647 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4648 compose->to_list = address_list_append(compose->to_list, entry);
4649 recipient_found = TRUE;
4652 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4653 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4654 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4655 recipient_found = TRUE;
4662 return recipient_found;
4665 static gboolean compose_check_for_set_recipients(Compose *compose)
4667 if (compose->account->set_autocc && compose->account->auto_cc) {
4668 gboolean found_other = FALSE;
4670 /* search header entries for to and newsgroup entries */
4671 for (list = compose->header_list; list; list = list->next) {
4674 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4675 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4678 if (strcmp(entry, compose->account->auto_cc)
4679 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4689 if (compose->batch) {
4690 gtk_widget_show_all(compose->window);
4692 aval = alertpanel(_("Send"),
4693 _("The only recipient is the default CC address. Send anyway?"),
4694 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4695 if (aval != G_ALERTALTERNATE)
4699 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4700 gboolean found_other = FALSE;
4702 /* search header entries for to and newsgroup entries */
4703 for (list = compose->header_list; list; list = list->next) {
4706 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4707 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4710 if (strcmp(entry, compose->account->auto_bcc)
4711 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4721 if (compose->batch) {
4722 gtk_widget_show_all(compose->window);
4724 aval = alertpanel(_("Send"),
4725 _("The only recipient is the default BCC address. Send anyway?"),
4726 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4727 if (aval != G_ALERTALTERNATE)
4734 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4738 if (compose_check_for_valid_recipient(compose) == FALSE) {
4739 if (compose->batch) {
4740 gtk_widget_show_all(compose->window);
4742 alertpanel_error(_("Recipient is not specified."));
4746 if (compose_check_for_set_recipients(compose) == FALSE) {
4750 if (!compose->batch) {
4751 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4752 if (*str == '\0' && check_everything == TRUE &&
4753 compose->mode != COMPOSE_REDIRECT) {
4755 gchar *button_label;
4758 if (compose->sending)
4759 button_label = _("+_Send");
4761 button_label = _("+_Queue");
4762 message = g_strdup_printf(_("Subject is empty. %s"),
4763 compose->sending?_("Send it anyway?"):
4764 _("Queue it anyway?"));
4766 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4767 GTK_STOCK_CANCEL, button_label, NULL);
4769 if (aval != G_ALERTALTERNATE)
4774 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4780 gint compose_send(Compose *compose)
4783 FolderItem *folder = NULL;
4785 gchar *msgpath = NULL;
4786 gboolean discard_window = FALSE;
4787 gchar *errstr = NULL;
4788 gchar *tmsgid = NULL;
4789 MainWindow *mainwin = mainwindow_get_mainwindow();
4790 gboolean queued_removed = FALSE;
4792 if (prefs_common.send_dialog_invisible
4793 || compose->batch == TRUE)
4794 discard_window = TRUE;
4796 compose_allow_user_actions (compose, FALSE);
4797 compose->sending = TRUE;
4799 if (compose_check_entries(compose, TRUE) == FALSE) {
4800 if (compose->batch) {
4801 gtk_widget_show_all(compose->window);
4807 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4810 if (compose->batch) {
4811 gtk_widget_show_all(compose->window);
4814 alertpanel_error(_("Could not queue message for sending:\n\n"
4815 "Charset conversion failed."));
4816 } else if (val == -5) {
4817 alertpanel_error(_("Could not queue message for sending:\n\n"
4818 "Couldn't get recipient encryption key."));
4819 } else if (val == -6) {
4821 } else if (val == -3) {
4822 if (privacy_peek_error())
4823 alertpanel_error(_("Could not queue message for sending:\n\n"
4824 "Signature failed: %s"), privacy_get_error());
4825 } else if (val == -2 && errno != 0) {
4826 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4828 alertpanel_error(_("Could not queue message for sending."));
4833 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4834 if (discard_window) {
4835 compose->sending = FALSE;
4836 compose_close(compose);
4837 /* No more compose access in the normal codepath
4838 * after this point! */
4843 alertpanel_error(_("The message was queued but could not be "
4844 "sent.\nUse \"Send queued messages\" from "
4845 "the main window to retry."));
4846 if (!discard_window) {
4853 if (msgpath == NULL) {
4854 msgpath = folder_item_fetch_msg(folder, msgnum);
4855 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4858 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4859 claws_unlink(msgpath);
4862 if (!discard_window) {
4864 if (!queued_removed)
4865 folder_item_remove_msg(folder, msgnum);
4866 folder_item_scan(folder);
4868 /* make sure we delete that */
4869 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4871 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4872 folder_item_remove_msg(folder, tmp->msgnum);
4873 procmsg_msginfo_free(tmp);
4880 if (!queued_removed)
4881 folder_item_remove_msg(folder, msgnum);
4882 folder_item_scan(folder);
4884 /* make sure we delete that */
4885 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4887 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4888 folder_item_remove_msg(folder, tmp->msgnum);
4889 procmsg_msginfo_free(tmp);
4892 if (!discard_window) {
4893 compose->sending = FALSE;
4894 compose_allow_user_actions (compose, TRUE);
4895 compose_close(compose);
4899 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4900 "the main window to retry."), errstr);
4903 alertpanel_error_log(_("The message was queued but could not be "
4904 "sent.\nUse \"Send queued messages\" from "
4905 "the main window to retry."));
4907 if (!discard_window) {
4916 toolbar_main_set_sensitive(mainwin);
4917 main_window_set_menu_sensitive(mainwin);
4923 compose_allow_user_actions (compose, TRUE);
4924 compose->sending = FALSE;
4925 compose->modified = TRUE;
4926 toolbar_main_set_sensitive(mainwin);
4927 main_window_set_menu_sensitive(mainwin);
4932 static gboolean compose_use_attach(Compose *compose)
4934 GtkTreeModel *model = gtk_tree_view_get_model
4935 (GTK_TREE_VIEW(compose->attach_clist));
4936 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4939 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4942 gchar buf[BUFFSIZE];
4944 gboolean first_to_address;
4945 gboolean first_cc_address;
4947 ComposeHeaderEntry *headerentry;
4948 const gchar *headerentryname;
4949 const gchar *cc_hdr;
4950 const gchar *to_hdr;
4951 gboolean err = FALSE;
4953 debug_print("Writing redirect header\n");
4955 cc_hdr = prefs_common_translated_header_name("Cc:");
4956 to_hdr = prefs_common_translated_header_name("To:");
4958 first_to_address = TRUE;
4959 for (list = compose->header_list; list; list = list->next) {
4960 headerentry = ((ComposeHeaderEntry *)list->data);
4961 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4963 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4964 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4965 Xstrdup_a(str, entstr, return -1);
4967 if (str[0] != '\0') {
4968 compose_convert_header
4969 (compose, buf, sizeof(buf), str,
4970 strlen("Resent-To") + 2, TRUE);
4972 if (first_to_address) {
4973 err |= (fprintf(fp, "Resent-To: ") < 0);
4974 first_to_address = FALSE;
4976 err |= (fprintf(fp, ",") < 0);
4978 err |= (fprintf(fp, "%s", buf) < 0);
4982 if (!first_to_address) {
4983 err |= (fprintf(fp, "\n") < 0);
4986 first_cc_address = TRUE;
4987 for (list = compose->header_list; list; list = list->next) {
4988 headerentry = ((ComposeHeaderEntry *)list->data);
4989 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4991 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4992 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4993 Xstrdup_a(str, strg, return -1);
4995 if (str[0] != '\0') {
4996 compose_convert_header
4997 (compose, buf, sizeof(buf), str,
4998 strlen("Resent-Cc") + 2, TRUE);
5000 if (first_cc_address) {
5001 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5002 first_cc_address = FALSE;
5004 err |= (fprintf(fp, ",") < 0);
5006 err |= (fprintf(fp, "%s", buf) < 0);
5010 if (!first_cc_address) {
5011 err |= (fprintf(fp, "\n") < 0);
5014 return (err ? -1:0);
5017 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5019 gchar buf[BUFFSIZE];
5021 const gchar *entstr;
5022 /* struct utsname utsbuf; */
5023 gboolean err = FALSE;
5025 cm_return_val_if_fail(fp != NULL, -1);
5026 cm_return_val_if_fail(compose->account != NULL, -1);
5027 cm_return_val_if_fail(compose->account->address != NULL, -1);
5030 get_rfc822_date(buf, sizeof(buf));
5031 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5034 if (compose->account->name && *compose->account->name) {
5035 compose_convert_header
5036 (compose, buf, sizeof(buf), compose->account->name,
5037 strlen("From: "), TRUE);
5038 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5039 buf, compose->account->address) < 0);
5041 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5044 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5045 if (*entstr != '\0') {
5046 Xstrdup_a(str, entstr, return -1);
5049 compose_convert_header(compose, buf, sizeof(buf), str,
5050 strlen("Subject: "), FALSE);
5051 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5055 /* Resent-Message-ID */
5056 if (compose->account->set_domain && compose->account->domain) {
5057 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5058 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5059 g_snprintf(buf, sizeof(buf), "%s",
5060 strchr(compose->account->address, '@') ?
5061 strchr(compose->account->address, '@')+1 :
5062 compose->account->address);
5064 g_snprintf(buf, sizeof(buf), "%s", "");
5067 if (compose->account->gen_msgid) {
5068 generate_msgid(buf, sizeof(buf));
5069 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5070 compose->msgid = g_strdup(buf);
5072 compose->msgid = NULL;
5075 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5078 /* separator between header and body */
5079 err |= (fputs("\n", fp) == EOF);
5081 return (err ? -1:0);
5084 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5088 gchar buf[BUFFSIZE];
5090 gboolean skip = FALSE;
5091 gboolean err = FALSE;
5092 gchar *not_included[]={
5093 "Return-Path:", "Delivered-To:", "Received:",
5094 "Subject:", "X-UIDL:", "AF:",
5095 "NF:", "PS:", "SRH:",
5096 "SFN:", "DSR:", "MID:",
5097 "CFG:", "PT:", "S:",
5098 "RQ:", "SSV:", "NSV:",
5099 "SSH:", "R:", "MAID:",
5100 "NAID:", "RMID:", "FMID:",
5101 "SCF:", "RRCPT:", "NG:",
5102 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5103 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5104 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5105 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5106 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5109 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5110 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5114 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5116 for (i = 0; not_included[i] != NULL; i++) {
5117 if (g_ascii_strncasecmp(buf, not_included[i],
5118 strlen(not_included[i])) == 0) {
5125 if (fputs(buf, fdest) == -1)
5128 if (!prefs_common.redirect_keep_from) {
5129 if (g_ascii_strncasecmp(buf, "From:",
5130 strlen("From:")) == 0) {
5131 err |= (fputs(" (by way of ", fdest) == EOF);
5132 if (compose->account->name
5133 && *compose->account->name) {
5134 compose_convert_header
5135 (compose, buf, sizeof(buf),
5136 compose->account->name,
5139 err |= (fprintf(fdest, "%s <%s>",
5141 compose->account->address) < 0);
5143 err |= (fprintf(fdest, "%s",
5144 compose->account->address) < 0);
5145 err |= (fputs(")", fdest) == EOF);
5149 if (fputs("\n", fdest) == -1)
5156 if (compose_redirect_write_headers(compose, fdest))
5159 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5160 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5173 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5175 GtkTextBuffer *buffer;
5176 GtkTextIter start, end;
5179 const gchar *out_codeset;
5180 EncodingType encoding = ENC_UNKNOWN;
5181 MimeInfo *mimemsg, *mimetext;
5183 const gchar *src_codeset = CS_INTERNAL;
5184 gchar *from_addr = NULL;
5185 gchar *from_name = NULL;
5187 if (action == COMPOSE_WRITE_FOR_SEND)
5188 attach_parts = TRUE;
5190 /* create message MimeInfo */
5191 mimemsg = procmime_mimeinfo_new();
5192 mimemsg->type = MIMETYPE_MESSAGE;
5193 mimemsg->subtype = g_strdup("rfc822");
5194 mimemsg->content = MIMECONTENT_MEM;
5195 mimemsg->tmp = TRUE; /* must free content later */
5196 mimemsg->data.mem = compose_get_header(compose);
5198 /* Create text part MimeInfo */
5199 /* get all composed text */
5200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5201 gtk_text_buffer_get_start_iter(buffer, &start);
5202 gtk_text_buffer_get_end_iter(buffer, &end);
5203 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5205 out_codeset = conv_get_charset_str(compose->out_encoding);
5207 if (!out_codeset && is_ascii_str(chars)) {
5208 out_codeset = CS_US_ASCII;
5209 } else if (prefs_common.outgoing_fallback_to_ascii &&
5210 is_ascii_str(chars)) {
5211 out_codeset = CS_US_ASCII;
5212 encoding = ENC_7BIT;
5216 gchar *test_conv_global_out = NULL;
5217 gchar *test_conv_reply = NULL;
5219 /* automatic mode. be automatic. */
5220 codeconv_set_strict(TRUE);
5222 out_codeset = conv_get_outgoing_charset_str();
5224 debug_print("trying to convert to %s\n", out_codeset);
5225 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5228 if (!test_conv_global_out && compose->orig_charset
5229 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5230 out_codeset = compose->orig_charset;
5231 debug_print("failure; trying to convert to %s\n", out_codeset);
5232 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5235 if (!test_conv_global_out && !test_conv_reply) {
5237 out_codeset = CS_INTERNAL;
5238 debug_print("failure; finally using %s\n", out_codeset);
5240 g_free(test_conv_global_out);
5241 g_free(test_conv_reply);
5242 codeconv_set_strict(FALSE);
5245 if (encoding == ENC_UNKNOWN) {
5246 if (prefs_common.encoding_method == CTE_BASE64)
5247 encoding = ENC_BASE64;
5248 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5249 encoding = ENC_QUOTED_PRINTABLE;
5250 else if (prefs_common.encoding_method == CTE_8BIT)
5251 encoding = ENC_8BIT;
5253 encoding = procmime_get_encoding_for_charset(out_codeset);
5256 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5257 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5259 if (action == COMPOSE_WRITE_FOR_SEND) {
5260 codeconv_set_strict(TRUE);
5261 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5262 codeconv_set_strict(FALSE);
5268 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5269 "to the specified %s charset.\n"
5270 "Send it as %s?"), out_codeset, src_codeset);
5271 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5272 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5275 if (aval != G_ALERTALTERNATE) {
5280 out_codeset = src_codeset;
5286 out_codeset = src_codeset;
5291 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5292 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5293 strstr(buf, "\nFrom ") != NULL) {
5294 encoding = ENC_QUOTED_PRINTABLE;
5298 mimetext = procmime_mimeinfo_new();
5299 mimetext->content = MIMECONTENT_MEM;
5300 mimetext->tmp = TRUE; /* must free content later */
5301 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5302 * and free the data, which we need later. */
5303 mimetext->data.mem = g_strdup(buf);
5304 mimetext->type = MIMETYPE_TEXT;
5305 mimetext->subtype = g_strdup("plain");
5306 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5307 g_strdup(out_codeset));
5309 /* protect trailing spaces when signing message */
5310 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5311 privacy_system_can_sign(compose->privacy_system)) {
5312 encoding = ENC_QUOTED_PRINTABLE;
5315 debug_print("main text: %zd bytes encoded as %s in %d\n",
5316 strlen(buf), out_codeset, encoding);
5318 /* check for line length limit */
5319 if (action == COMPOSE_WRITE_FOR_SEND &&
5320 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5321 check_line_length(buf, 1000, &line) < 0) {
5325 msg = g_strdup_printf
5326 (_("Line %d exceeds the line length limit (998 bytes).\n"
5327 "The contents of the message might be broken on the way to the delivery.\n"
5329 "Send it anyway?"), line + 1);
5330 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5332 if (aval != G_ALERTALTERNATE) {
5338 if (encoding != ENC_UNKNOWN)
5339 procmime_encode_content(mimetext, encoding);
5341 /* append attachment parts */
5342 if (compose_use_attach(compose) && attach_parts) {
5343 MimeInfo *mimempart;
5344 gchar *boundary = NULL;
5345 mimempart = procmime_mimeinfo_new();
5346 mimempart->content = MIMECONTENT_EMPTY;
5347 mimempart->type = MIMETYPE_MULTIPART;
5348 mimempart->subtype = g_strdup("mixed");
5352 boundary = generate_mime_boundary(NULL);
5353 } while (strstr(buf, boundary) != NULL);
5355 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5358 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5360 g_node_append(mimempart->node, mimetext->node);
5361 g_node_append(mimemsg->node, mimempart->node);
5363 if (compose_add_attachments(compose, mimempart) < 0)
5366 g_node_append(mimemsg->node, mimetext->node);
5370 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5371 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5372 /* extract name and address */
5373 if (strstr(spec, " <") && strstr(spec, ">")) {
5374 from_addr = g_strdup(strrchr(spec, '<')+1);
5375 *(strrchr(from_addr, '>')) = '\0';
5376 from_name = g_strdup(spec);
5377 *(strrchr(from_name, '<')) = '\0';
5384 /* sign message if sending */
5385 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5386 privacy_system_can_sign(compose->privacy_system))
5387 if (!privacy_sign(compose->privacy_system, mimemsg,
5388 compose->account, from_addr)) {
5395 procmime_write_mimeinfo(mimemsg, fp);
5397 procmime_mimeinfo_free_all(mimemsg);
5402 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5404 GtkTextBuffer *buffer;
5405 GtkTextIter start, end;
5410 if ((fp = g_fopen(file, "wb")) == NULL) {
5411 FILE_OP_ERROR(file, "fopen");
5415 /* chmod for security */
5416 if (change_file_mode_rw(fp, file) < 0) {
5417 FILE_OP_ERROR(file, "chmod");
5418 g_warning("can't change file mode\n");
5421 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5422 gtk_text_buffer_get_start_iter(buffer, &start);
5423 gtk_text_buffer_get_end_iter(buffer, &end);
5424 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5426 chars = conv_codeset_strdup
5427 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5430 if (!chars) return -1;
5433 len = strlen(chars);
5434 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5435 FILE_OP_ERROR(file, "fwrite");
5444 if (fclose(fp) == EOF) {
5445 FILE_OP_ERROR(file, "fclose");
5452 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5455 MsgInfo *msginfo = compose->targetinfo;
5457 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5458 if (!msginfo) return -1;
5460 if (!force && MSG_IS_LOCKED(msginfo->flags))
5463 item = msginfo->folder;
5464 cm_return_val_if_fail(item != NULL, -1);
5466 if (procmsg_msg_exist(msginfo) &&
5467 (folder_has_parent_of_type(item, F_QUEUE) ||
5468 folder_has_parent_of_type(item, F_DRAFT)
5469 || msginfo == compose->autosaved_draft)) {
5470 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5471 g_warning("can't remove the old message\n");
5474 debug_print("removed reedit target %d\n", msginfo->msgnum);
5481 static void compose_remove_draft(Compose *compose)
5484 MsgInfo *msginfo = compose->targetinfo;
5485 drafts = account_get_special_folder(compose->account, F_DRAFT);
5487 if (procmsg_msg_exist(msginfo)) {
5488 folder_item_remove_msg(drafts, msginfo->msgnum);
5493 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5494 gboolean remove_reedit_target)
5496 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5499 static gboolean compose_warn_encryption(Compose *compose)
5501 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5502 AlertValue val = G_ALERTALTERNATE;
5504 if (warning == NULL)
5507 val = alertpanel_full(_("Encryption warning"), warning,
5508 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5509 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5510 if (val & G_ALERTDISABLE) {
5511 val &= ~G_ALERTDISABLE;
5512 if (val == G_ALERTALTERNATE)
5513 privacy_inhibit_encrypt_warning(compose->privacy_system,
5517 if (val == G_ALERTALTERNATE) {
5524 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5525 gchar **msgpath, gboolean check_subject,
5526 gboolean remove_reedit_target)
5533 static gboolean lock = FALSE;
5534 PrefsAccount *mailac = NULL, *newsac = NULL;
5535 gboolean err = FALSE;
5537 debug_print("queueing message...\n");
5538 cm_return_val_if_fail(compose->account != NULL, -1);
5542 if (compose_check_entries(compose, check_subject) == FALSE) {
5544 if (compose->batch) {
5545 gtk_widget_show_all(compose->window);
5550 if (!compose->to_list && !compose->newsgroup_list) {
5551 g_warning("can't get recipient list.");
5556 if (compose->to_list) {
5557 if (compose->account->protocol != A_NNTP)
5558 mailac = compose->account;
5559 else if (cur_account && cur_account->protocol != A_NNTP)
5560 mailac = cur_account;
5561 else if (!(mailac = compose_current_mail_account())) {
5563 alertpanel_error(_("No account for sending mails available!"));
5568 if (compose->newsgroup_list) {
5569 if (compose->account->protocol == A_NNTP)
5570 newsac = compose->account;
5573 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5578 /* write queue header */
5579 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5580 G_DIR_SEPARATOR, compose, (guint) rand());
5581 debug_print("queuing to %s\n", tmp);
5582 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5583 FILE_OP_ERROR(tmp, "fopen");
5589 if (change_file_mode_rw(fp, tmp) < 0) {
5590 FILE_OP_ERROR(tmp, "chmod");
5591 g_warning("can't change file mode\n");
5594 /* queueing variables */
5595 err |= (fprintf(fp, "AF:\n") < 0);
5596 err |= (fprintf(fp, "NF:0\n") < 0);
5597 err |= (fprintf(fp, "PS:10\n") < 0);
5598 err |= (fprintf(fp, "SRH:1\n") < 0);
5599 err |= (fprintf(fp, "SFN:\n") < 0);
5600 err |= (fprintf(fp, "DSR:\n") < 0);
5602 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5604 err |= (fprintf(fp, "MID:\n") < 0);
5605 err |= (fprintf(fp, "CFG:\n") < 0);
5606 err |= (fprintf(fp, "PT:0\n") < 0);
5607 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5608 err |= (fprintf(fp, "RQ:\n") < 0);
5610 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5612 err |= (fprintf(fp, "SSV:\n") < 0);
5614 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5616 err |= (fprintf(fp, "NSV:\n") < 0);
5617 err |= (fprintf(fp, "SSH:\n") < 0);
5618 /* write recepient list */
5619 if (compose->to_list) {
5620 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5621 for (cur = compose->to_list->next; cur != NULL;
5623 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5624 err |= (fprintf(fp, "\n") < 0);
5626 /* write newsgroup list */
5627 if (compose->newsgroup_list) {
5628 err |= (fprintf(fp, "NG:") < 0);
5629 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5630 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5631 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5632 err |= (fprintf(fp, "\n") < 0);
5634 /* Sylpheed account IDs */
5636 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5638 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5641 if (compose->privacy_system != NULL) {
5642 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5643 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5644 if (compose->use_encryption) {
5646 if (!compose_warn_encryption(compose)) {
5653 if (mailac && mailac->encrypt_to_self) {
5654 GSList *tmp_list = g_slist_copy(compose->to_list);
5655 tmp_list = g_slist_append(tmp_list, compose->account->address);
5656 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5657 g_slist_free(tmp_list);
5659 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5661 if (encdata != NULL) {
5662 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5663 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5664 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5666 } /* else we finally dont want to encrypt */
5668 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5669 /* and if encdata was null, it means there's been a problem in
5681 /* Save copy folder */
5682 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5683 gchar *savefolderid;
5685 savefolderid = compose_get_save_to(compose);
5686 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5687 g_free(savefolderid);
5689 /* Save copy folder */
5690 if (compose->return_receipt) {
5691 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5693 /* Message-ID of message replying to */
5694 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5697 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5698 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5701 /* Message-ID of message forwarding to */
5702 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5705 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5706 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5710 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5711 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5713 /* end of headers */
5714 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5716 if (compose->redirect_filename != NULL) {
5717 if (compose_redirect_write_to_file(compose, fp) < 0) {
5726 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5731 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5735 g_warning("failed to write queue message\n");
5742 if (fclose(fp) == EOF) {
5743 FILE_OP_ERROR(tmp, "fclose");
5750 if (item && *item) {
5753 queue = account_get_special_folder(compose->account, F_QUEUE);
5756 g_warning("can't find queue folder\n");
5762 folder_item_scan(queue);
5763 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5764 g_warning("can't queue the message\n");
5771 if (msgpath == NULL) {
5777 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5778 compose_remove_reedit_target(compose, FALSE);
5781 if ((msgnum != NULL) && (item != NULL)) {
5789 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5792 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5794 struct stat statbuf;
5795 gchar *type, *subtype;
5796 GtkTreeModel *model;
5799 model = gtk_tree_view_get_model(tree_view);
5801 if (!gtk_tree_model_get_iter_first(model, &iter))
5804 gtk_tree_model_get(model, &iter,
5808 if (!is_file_exist(ainfo->file)) {
5809 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5810 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5811 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5813 if (val == G_ALERTDEFAULT) {
5818 mimepart = procmime_mimeinfo_new();
5819 mimepart->content = MIMECONTENT_FILE;
5820 mimepart->data.filename = g_strdup(ainfo->file);
5821 mimepart->tmp = FALSE; /* or we destroy our attachment */
5822 mimepart->offset = 0;
5824 g_stat(ainfo->file, &statbuf);
5825 mimepart->length = statbuf.st_size;
5827 type = g_strdup(ainfo->content_type);
5829 if (!strchr(type, '/')) {
5831 type = g_strdup("application/octet-stream");
5834 subtype = strchr(type, '/') + 1;
5835 *(subtype - 1) = '\0';
5836 mimepart->type = procmime_get_media_type(type);
5837 mimepart->subtype = g_strdup(subtype);
5840 if (mimepart->type == MIMETYPE_MESSAGE &&
5841 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5842 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5845 if (mimepart->type == MIMETYPE_APPLICATION &&
5846 !strcmp2(mimepart->subtype, "octet-stream"))
5847 g_hash_table_insert(mimepart->typeparameters,
5848 g_strdup("name"), g_strdup(ainfo->name));
5849 g_hash_table_insert(mimepart->dispositionparameters,
5850 g_strdup("filename"), g_strdup(ainfo->name));
5851 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5855 if (compose->use_signing) {
5856 if (ainfo->encoding == ENC_7BIT)
5857 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5858 else if (ainfo->encoding == ENC_8BIT)
5859 ainfo->encoding = ENC_BASE64;
5862 procmime_encode_content(mimepart, ainfo->encoding);
5864 g_node_append(parent->node, mimepart->node);
5865 } while (gtk_tree_model_iter_next(model, &iter));
5870 #define IS_IN_CUSTOM_HEADER(header) \
5871 (compose->account->add_customhdr && \
5872 custom_header_find(compose->account->customhdr_list, header) != NULL)
5874 static void compose_add_headerfield_from_headerlist(Compose *compose,
5876 const gchar *fieldname,
5877 const gchar *seperator)
5879 gchar *str, *fieldname_w_colon;
5880 gboolean add_field = FALSE;
5882 ComposeHeaderEntry *headerentry;
5883 const gchar *headerentryname;
5884 const gchar *trans_fieldname;
5887 if (IS_IN_CUSTOM_HEADER(fieldname))
5890 debug_print("Adding %s-fields\n", fieldname);
5892 fieldstr = g_string_sized_new(64);
5894 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5895 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5897 for (list = compose->header_list; list; list = list->next) {
5898 headerentry = ((ComposeHeaderEntry *)list->data);
5899 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5901 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5902 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5904 if (str[0] != '\0') {
5906 g_string_append(fieldstr, seperator);
5907 g_string_append(fieldstr, str);
5916 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5917 compose_convert_header
5918 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5919 strlen(fieldname) + 2, TRUE);
5920 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5924 g_free(fieldname_w_colon);
5925 g_string_free(fieldstr, TRUE);
5930 static gchar *compose_get_header(Compose *compose)
5932 gchar buf[BUFFSIZE];
5933 const gchar *entry_str;
5937 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5939 gchar *from_name = NULL, *from_address = NULL;
5942 cm_return_val_if_fail(compose->account != NULL, NULL);
5943 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5945 header = g_string_sized_new(64);
5948 get_rfc822_date(buf, sizeof(buf));
5949 g_string_append_printf(header, "Date: %s\n", buf);
5953 if (compose->account->name && *compose->account->name) {
5955 QUOTE_IF_REQUIRED(buf, compose->account->name);
5956 tmp = g_strdup_printf("%s <%s>",
5957 buf, compose->account->address);
5959 tmp = g_strdup_printf("%s",
5960 compose->account->address);
5962 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5963 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5965 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5966 from_address = g_strdup(compose->account->address);
5968 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5969 /* extract name and address */
5970 if (strstr(spec, " <") && strstr(spec, ">")) {
5971 from_address = g_strdup(strrchr(spec, '<')+1);
5972 *(strrchr(from_address, '>')) = '\0';
5973 from_name = g_strdup(spec);
5974 *(strrchr(from_name, '<')) = '\0';
5977 from_address = g_strdup(spec);
5984 if (from_name && *from_name) {
5985 compose_convert_header
5986 (compose, buf, sizeof(buf), from_name,
5987 strlen("From: "), TRUE);
5988 QUOTE_IF_REQUIRED(name, buf);
5990 g_string_append_printf(header, "From: %s <%s>\n",
5991 name, from_address);
5993 g_string_append_printf(header, "From: %s\n", from_address);
5996 g_free(from_address);
5999 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6002 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6005 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6008 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6011 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6013 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6016 compose_convert_header(compose, buf, sizeof(buf), str,
6017 strlen("Subject: "), FALSE);
6018 g_string_append_printf(header, "Subject: %s\n", buf);
6024 if (compose->account->set_domain && compose->account->domain) {
6025 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6026 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6027 g_snprintf(buf, sizeof(buf), "%s",
6028 strchr(compose->account->address, '@') ?
6029 strchr(compose->account->address, '@')+1 :
6030 compose->account->address);
6032 g_snprintf(buf, sizeof(buf), "%s", "");
6035 if (compose->account->gen_msgid) {
6036 generate_msgid(buf, sizeof(buf));
6037 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6038 compose->msgid = g_strdup(buf);
6040 compose->msgid = NULL;
6043 if (compose->remove_references == FALSE) {
6045 if (compose->inreplyto && compose->to_list)
6046 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6049 if (compose->references)
6050 g_string_append_printf(header, "References: %s\n", compose->references);
6054 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6057 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6060 if (compose->account->organization &&
6061 strlen(compose->account->organization) &&
6062 !IS_IN_CUSTOM_HEADER("Organization")) {
6063 compose_convert_header(compose, buf, sizeof(buf),
6064 compose->account->organization,
6065 strlen("Organization: "), FALSE);
6066 g_string_append_printf(header, "Organization: %s\n", buf);
6069 /* Program version and system info */
6070 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6071 !compose->newsgroup_list) {
6072 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6074 gtk_major_version, gtk_minor_version, gtk_micro_version,
6077 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6078 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6080 gtk_major_version, gtk_minor_version, gtk_micro_version,
6084 /* custom headers */
6085 if (compose->account->add_customhdr) {
6088 for (cur = compose->account->customhdr_list; cur != NULL;
6090 CustomHeader *chdr = (CustomHeader *)cur->data;
6092 if (custom_header_is_allowed(chdr->name)) {
6093 compose_convert_header
6094 (compose, buf, sizeof(buf),
6095 chdr->value ? chdr->value : "",
6096 strlen(chdr->name) + 2, FALSE);
6097 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6102 /* Automatic Faces and X-Faces */
6103 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6104 g_string_append_printf(header, "X-Face: %s\n", buf);
6106 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6107 g_string_append_printf(header, "X-Face: %s\n", buf);
6109 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6110 g_string_append_printf(header, "Face: %s\n", buf);
6112 else if (get_default_face (buf, sizeof(buf)) == 0) {
6113 g_string_append_printf(header, "Face: %s\n", buf);
6117 switch (compose->priority) {
6118 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6119 "X-Priority: 1 (Highest)\n");
6121 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6122 "X-Priority: 2 (High)\n");
6124 case PRIORITY_NORMAL: break;
6125 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6126 "X-Priority: 4 (Low)\n");
6128 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6129 "X-Priority: 5 (Lowest)\n");
6131 default: debug_print("compose: priority unknown : %d\n",
6135 /* Request Return Receipt */
6136 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6137 if (compose->return_receipt) {
6138 if (compose->account->name
6139 && *compose->account->name) {
6140 compose_convert_header(compose, buf, sizeof(buf),
6141 compose->account->name,
6142 strlen("Disposition-Notification-To: "),
6144 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6146 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6150 /* get special headers */
6151 for (list = compose->header_list; list; list = list->next) {
6152 ComposeHeaderEntry *headerentry;
6155 gchar *headername_wcolon;
6156 const gchar *headername_trans;
6159 gboolean standard_header = FALSE;
6161 headerentry = ((ComposeHeaderEntry *)list->data);
6163 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6165 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6170 if (!strstr(tmp, ":")) {
6171 headername_wcolon = g_strconcat(tmp, ":", NULL);
6172 headername = g_strdup(tmp);
6174 headername_wcolon = g_strdup(tmp);
6175 headername = g_strdup(strtok(tmp, ":"));
6179 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6180 Xstrdup_a(headervalue, entry_str, return NULL);
6181 subst_char(headervalue, '\r', ' ');
6182 subst_char(headervalue, '\n', ' ');
6183 string = std_headers;
6184 while (*string != NULL) {
6185 headername_trans = prefs_common_translated_header_name(*string);
6186 if (!strcmp(headername_trans, headername_wcolon))
6187 standard_header = TRUE;
6190 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6191 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6194 g_free(headername_wcolon);
6198 g_string_free(header, FALSE);
6203 #undef IS_IN_CUSTOM_HEADER
6205 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6206 gint header_len, gboolean addr_field)
6208 gchar *tmpstr = NULL;
6209 const gchar *out_codeset = NULL;
6211 cm_return_if_fail(src != NULL);
6212 cm_return_if_fail(dest != NULL);
6214 if (len < 1) return;
6216 tmpstr = g_strdup(src);
6218 subst_char(tmpstr, '\n', ' ');
6219 subst_char(tmpstr, '\r', ' ');
6222 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6223 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6224 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6229 codeconv_set_strict(TRUE);
6230 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6231 conv_get_charset_str(compose->out_encoding));
6232 codeconv_set_strict(FALSE);
6234 if (!dest || *dest == '\0') {
6235 gchar *test_conv_global_out = NULL;
6236 gchar *test_conv_reply = NULL;
6238 /* automatic mode. be automatic. */
6239 codeconv_set_strict(TRUE);
6241 out_codeset = conv_get_outgoing_charset_str();
6243 debug_print("trying to convert to %s\n", out_codeset);
6244 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6247 if (!test_conv_global_out && compose->orig_charset
6248 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6249 out_codeset = compose->orig_charset;
6250 debug_print("failure; trying to convert to %s\n", out_codeset);
6251 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6254 if (!test_conv_global_out && !test_conv_reply) {
6256 out_codeset = CS_INTERNAL;
6257 debug_print("finally using %s\n", out_codeset);
6259 g_free(test_conv_global_out);
6260 g_free(test_conv_reply);
6261 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6263 codeconv_set_strict(FALSE);
6268 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6272 cm_return_if_fail(user_data != NULL);
6274 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6275 g_strstrip(address);
6276 if (*address != '\0') {
6277 gchar *name = procheader_get_fromname(address);
6278 extract_address(address);
6279 addressbook_add_contact(name, address, NULL, NULL);
6284 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6286 GtkWidget *menuitem;
6289 cm_return_if_fail(menu != NULL);
6290 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6292 menuitem = gtk_separator_menu_item_new();
6293 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6294 gtk_widget_show(menuitem);
6296 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6297 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6299 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6300 g_strstrip(address);
6301 if (*address == '\0') {
6302 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6305 g_signal_connect(G_OBJECT(menuitem), "activate",
6306 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6307 gtk_widget_show(menuitem);
6310 static void compose_create_header_entry(Compose *compose)
6312 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6319 const gchar *header = NULL;
6320 ComposeHeaderEntry *headerentry;
6321 gboolean standard_header = FALSE;
6322 #if !(GTK_CHECK_VERSION(2,12,0))
6323 GtkTooltips *tips = compose->tooltips;
6326 headerentry = g_new0(ComposeHeaderEntry, 1);
6329 combo = gtk_combo_box_entry_new_text();
6331 while(*string != NULL) {
6332 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6333 (gchar*)prefs_common_translated_header_name(*string));
6336 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6337 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6338 G_CALLBACK(compose_grab_focus_cb), compose);
6339 gtk_widget_show(combo);
6340 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6341 compose->header_nextrow, compose->header_nextrow+1,
6342 GTK_SHRINK, GTK_FILL, 0, 0);
6343 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6344 const gchar *last_header_entry = gtk_entry_get_text(
6345 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6347 while (*string != NULL) {
6348 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6349 standard_header = TRUE;
6352 if (standard_header)
6353 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6355 if (!compose->header_last || !standard_header) {
6356 switch(compose->account->protocol) {
6358 header = prefs_common_translated_header_name("Newsgroups:");
6361 header = prefs_common_translated_header_name("To:");
6366 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6368 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6369 G_CALLBACK(compose_grab_focus_cb), compose);
6371 /* Entry field with cleanup button */
6372 button = gtk_button_new();
6373 gtk_button_set_image(GTK_BUTTON(button),
6374 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6375 gtk_widget_show(button);
6376 CLAWS_SET_TIP(button,
6377 _("Delete entry contents"));
6378 entry = gtk_entry_new();
6379 gtk_widget_show(entry);
6380 CLAWS_SET_TIP(entry,
6381 _("Use <tab> to autocomplete from addressbook"));
6382 hbox = gtk_hbox_new (FALSE, 0);
6383 gtk_widget_show(hbox);
6384 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6385 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6386 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6387 compose->header_nextrow, compose->header_nextrow+1,
6388 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6390 g_signal_connect(G_OBJECT(entry), "key-press-event",
6391 G_CALLBACK(compose_headerentry_key_press_event_cb),
6393 g_signal_connect(G_OBJECT(entry), "changed",
6394 G_CALLBACK(compose_headerentry_changed_cb),
6396 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6397 G_CALLBACK(compose_grab_focus_cb), compose);
6399 g_signal_connect(G_OBJECT(button), "clicked",
6400 G_CALLBACK(compose_headerentry_button_clicked_cb),
6404 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6405 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6406 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6407 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6408 G_CALLBACK(compose_header_drag_received_cb),
6410 g_signal_connect(G_OBJECT(entry), "drag-drop",
6411 G_CALLBACK(compose_drag_drop),
6413 g_signal_connect(G_OBJECT(entry), "populate-popup",
6414 G_CALLBACK(compose_entry_popup_extend),
6417 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6419 headerentry->compose = compose;
6420 headerentry->combo = combo;
6421 headerentry->entry = entry;
6422 headerentry->button = button;
6423 headerentry->hbox = hbox;
6424 headerentry->headernum = compose->header_nextrow;
6426 compose->header_nextrow++;
6427 compose->header_last = headerentry;
6428 compose->header_list =
6429 g_slist_append(compose->header_list,
6433 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6435 ComposeHeaderEntry *last_header;
6437 last_header = compose->header_last;
6439 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6440 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6443 static void compose_remove_header_entries(Compose *compose)
6446 for (list = compose->header_list; list; list = list->next) {
6447 ComposeHeaderEntry *headerentry =
6448 (ComposeHeaderEntry *)list->data;
6449 gtk_widget_destroy(headerentry->combo);
6450 gtk_widget_destroy(headerentry->entry);
6451 gtk_widget_destroy(headerentry->button);
6452 g_free(headerentry);
6454 compose->header_last = NULL;
6455 g_slist_free(compose->header_list);
6456 compose->header_list = NULL;
6457 compose->header_nextrow = 1;
6458 compose_create_header_entry(compose);
6461 static GtkWidget *compose_create_header(Compose *compose)
6463 GtkWidget *from_optmenu_hbox;
6464 GtkWidget *header_scrolledwin;
6465 GtkWidget *header_table;
6469 /* header labels and entries */
6470 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6471 gtk_widget_show(header_scrolledwin);
6472 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6474 header_table = gtk_table_new(2, 2, FALSE);
6475 gtk_widget_show(header_table);
6476 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6477 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6478 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6481 /* option menu for selecting accounts */
6482 from_optmenu_hbox = compose_account_option_menu_create(compose);
6483 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6484 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6487 compose->header_table = header_table;
6488 compose->header_list = NULL;
6489 compose->header_nextrow = count;
6491 compose_create_header_entry(compose);
6493 compose->table = NULL;
6495 return header_scrolledwin ;
6498 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6500 Compose *compose = (Compose *)data;
6501 GdkEventButton event;
6504 event.time = gtk_get_current_event_time();
6506 return attach_button_pressed(compose->attach_clist, &event, compose);
6509 static GtkWidget *compose_create_attach(Compose *compose)
6511 GtkWidget *attach_scrwin;
6512 GtkWidget *attach_clist;
6514 GtkListStore *store;
6515 GtkCellRenderer *renderer;
6516 GtkTreeViewColumn *column;
6517 GtkTreeSelection *selection;
6519 /* attachment list */
6520 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6521 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6522 GTK_POLICY_AUTOMATIC,
6523 GTK_POLICY_AUTOMATIC);
6524 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6526 store = gtk_list_store_new(N_ATTACH_COLS,
6531 G_TYPE_AUTO_POINTER,
6533 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6534 (GTK_TREE_MODEL(store)));
6535 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6536 g_object_unref(store);
6538 renderer = gtk_cell_renderer_text_new();
6539 column = gtk_tree_view_column_new_with_attributes
6540 (_("Mime type"), renderer, "text",
6541 COL_MIMETYPE, NULL);
6542 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6544 renderer = gtk_cell_renderer_text_new();
6545 column = gtk_tree_view_column_new_with_attributes
6546 (_("Size"), renderer, "text",
6548 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6550 renderer = gtk_cell_renderer_text_new();
6551 column = gtk_tree_view_column_new_with_attributes
6552 (_("Name"), renderer, "text",
6554 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6556 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6557 prefs_common.use_stripes_everywhere);
6558 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6559 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6561 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6562 G_CALLBACK(attach_selected), compose);
6563 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6564 G_CALLBACK(attach_button_pressed), compose);
6566 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6567 G_CALLBACK(popup_attach_button_pressed), compose);
6569 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6570 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6571 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6572 G_CALLBACK(popup_attach_button_pressed), compose);
6574 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6575 G_CALLBACK(attach_key_pressed), compose);
6578 gtk_drag_dest_set(attach_clist,
6579 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6580 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6581 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6582 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6583 G_CALLBACK(compose_attach_drag_received_cb),
6585 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6586 G_CALLBACK(compose_drag_drop),
6589 compose->attach_scrwin = attach_scrwin;
6590 compose->attach_clist = attach_clist;
6592 return attach_scrwin;
6595 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6596 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6598 static GtkWidget *compose_create_others(Compose *compose)
6601 GtkWidget *savemsg_checkbtn;
6602 GtkWidget *savemsg_combo;
6603 GtkWidget *savemsg_select;
6606 gchar *folderidentifier;
6608 /* Table for settings */
6609 table = gtk_table_new(3, 1, FALSE);
6610 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6611 gtk_widget_show(table);
6612 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6615 /* Save Message to folder */
6616 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6617 gtk_widget_show(savemsg_checkbtn);
6618 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6619 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6620 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6622 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6623 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6625 savemsg_combo = gtk_combo_box_entry_new_text();
6626 compose->savemsg_checkbtn = savemsg_checkbtn;
6627 compose->savemsg_combo = savemsg_combo;
6628 gtk_widget_show(savemsg_combo);
6630 if (prefs_common.compose_save_to_history)
6631 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6632 prefs_common.compose_save_to_history);
6634 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6635 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6636 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6637 G_CALLBACK(compose_grab_focus_cb), compose);
6638 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6639 folderidentifier = folder_item_get_identifier(account_get_special_folder
6640 (compose->account, F_OUTBOX));
6641 compose_set_save_to(compose, folderidentifier);
6642 g_free(folderidentifier);
6645 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6646 gtk_widget_show(savemsg_select);
6647 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6648 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6649 G_CALLBACK(compose_savemsg_select_cb),
6657 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6659 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6660 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6663 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6668 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6671 path = folder_item_get_identifier(dest);
6673 compose_set_save_to(compose, path);
6677 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6678 GdkAtom clip, GtkTextIter *insert_place);
6681 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6685 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6687 if (event->button == 3) {
6689 GtkTextIter sel_start, sel_end;
6690 gboolean stuff_selected;
6692 /* move the cursor to allow GtkAspell to check the word
6693 * under the mouse */
6694 if (event->x && event->y) {
6695 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6696 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6698 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6701 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6702 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6705 stuff_selected = gtk_text_buffer_get_selection_bounds(
6707 &sel_start, &sel_end);
6709 gtk_text_buffer_place_cursor (buffer, &iter);
6710 /* reselect stuff */
6712 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6713 gtk_text_buffer_select_range(buffer,
6714 &sel_start, &sel_end);
6716 return FALSE; /* pass the event so that the right-click goes through */
6719 if (event->button == 2) {
6724 /* get the middle-click position to paste at the correct place */
6725 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6726 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6728 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6731 entry_paste_clipboard(compose, text,
6732 prefs_common.linewrap_pastes,
6733 GDK_SELECTION_PRIMARY, &iter);
6741 static void compose_spell_menu_changed(void *data)
6743 Compose *compose = (Compose *)data;
6745 GtkWidget *menuitem;
6746 GtkWidget *parent_item;
6747 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6750 if (compose->gtkaspell == NULL)
6753 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6754 "/Menu/Spelling/Options");
6756 /* setting the submenu removes /Spelling/Options from the factory
6757 * so we need to save it */
6759 if (parent_item == NULL) {
6760 parent_item = compose->aspell_options_menu;
6761 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6763 compose->aspell_options_menu = parent_item;
6765 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6767 spell_menu = g_slist_reverse(spell_menu);
6768 for (items = spell_menu;
6769 items; items = items->next) {
6770 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6771 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6772 gtk_widget_show(GTK_WIDGET(menuitem));
6774 g_slist_free(spell_menu);
6776 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6777 gtk_widget_show(parent_item);
6780 static void compose_dict_changed(void *data)
6782 Compose *compose = (Compose *) data;
6784 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6787 gtkaspell_highlight_all(compose->gtkaspell);
6788 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6792 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6794 Compose *compose = (Compose *)data;
6795 GdkEventButton event;
6798 event.time = gtk_get_current_event_time();
6802 return text_clicked(compose->text, &event, compose);
6805 static gboolean compose_force_window_origin = TRUE;
6806 static Compose *compose_create(PrefsAccount *account,
6815 GtkWidget *handlebox;
6817 GtkWidget *notebook;
6819 GtkWidget *attach_hbox;
6820 GtkWidget *attach_lab1;
6821 GtkWidget *attach_lab2;
6826 GtkWidget *subject_hbox;
6827 GtkWidget *subject_frame;
6828 GtkWidget *subject_entry;
6832 GtkWidget *edit_vbox;
6833 GtkWidget *ruler_hbox;
6835 GtkWidget *scrolledwin;
6837 GtkTextBuffer *buffer;
6838 GtkClipboard *clipboard;
6841 UndoMain *undostruct;
6843 gchar *titles[N_ATTACH_COLS];
6844 GtkWidget *popupmenu;
6845 GtkWidget *tmpl_menu;
6846 GtkActionGroup *action_group = NULL;
6849 GtkAspell * gtkaspell = NULL;
6852 static GdkGeometry geometry;
6854 cm_return_val_if_fail(account != NULL, NULL);
6856 debug_print("Creating compose window...\n");
6857 compose = g_new0(Compose, 1);
6859 titles[COL_MIMETYPE] = _("MIME type");
6860 titles[COL_SIZE] = _("Size");
6861 titles[COL_NAME] = _("Name");
6863 compose->batch = batch;
6864 compose->account = account;
6865 compose->folder = folder;
6867 compose->mutex = g_mutex_new();
6868 compose->set_cursor_pos = -1;
6870 #if !(GTK_CHECK_VERSION(2,12,0))
6871 compose->tooltips = tips;
6874 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6876 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6877 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6879 if (!geometry.max_width) {
6880 geometry.max_width = gdk_screen_width();
6881 geometry.max_height = gdk_screen_height();
6884 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6885 &geometry, GDK_HINT_MAX_SIZE);
6886 if (!geometry.min_width) {
6887 geometry.min_width = 600;
6888 geometry.min_height = 440;
6890 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6891 &geometry, GDK_HINT_MIN_SIZE);
6893 #ifndef GENERIC_UMPC
6894 if (compose_force_window_origin)
6895 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6896 prefs_common.compose_y);
6898 g_signal_connect(G_OBJECT(window), "delete_event",
6899 G_CALLBACK(compose_delete_cb), compose);
6900 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6901 gtk_widget_realize(window);
6903 gtkut_widget_set_composer_icon(window);
6905 vbox = gtk_vbox_new(FALSE, 0);
6906 gtk_container_add(GTK_CONTAINER(window), vbox);
6908 compose->ui_manager = gtk_ui_manager_new();
6909 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6910 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6911 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6912 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6913 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6914 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6915 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6916 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6917 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6918 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7002 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7003 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7004 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7010 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7012 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7038 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)
7039 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)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7045 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)
7046 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)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7051 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)
7052 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7055 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)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7061 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)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7064 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7067 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)
7068 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)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7080 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)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7093 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7098 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7099 gtk_widget_show_all(menubar);
7101 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7103 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7105 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7108 if (prefs_common.toolbar_detachable) {
7109 handlebox = gtk_handle_box_new();
7111 handlebox = gtk_hbox_new(FALSE, 0);
7113 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7115 gtk_widget_realize(handlebox);
7117 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7120 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7124 vbox2 = gtk_vbox_new(FALSE, 2);
7125 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7126 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7129 notebook = gtk_notebook_new();
7130 gtk_widget_set_size_request(notebook, -1, 130);
7131 gtk_widget_show(notebook);
7133 /* header labels and entries */
7134 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7135 compose_create_header(compose),
7136 gtk_label_new_with_mnemonic(_("Hea_der")));
7137 /* attachment list */
7138 attach_hbox = gtk_hbox_new(FALSE, 0);
7139 gtk_widget_show(attach_hbox);
7141 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7142 gtk_widget_show(attach_lab1);
7143 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7145 attach_lab2 = gtk_label_new("");
7146 gtk_widget_show(attach_lab2);
7147 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7149 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7150 compose_create_attach(compose),
7153 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7154 compose_create_others(compose),
7155 gtk_label_new_with_mnemonic(_("Othe_rs")));
7158 subject_hbox = gtk_hbox_new(FALSE, 0);
7159 gtk_widget_show(subject_hbox);
7161 subject_frame = gtk_frame_new(NULL);
7162 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7163 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7164 gtk_widget_show(subject_frame);
7166 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7167 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7168 gtk_widget_show(subject);
7170 label = gtk_label_new(_("Subject:"));
7171 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7172 gtk_widget_show(label);
7175 subject_entry = claws_spell_entry_new();
7177 subject_entry = gtk_entry_new();
7179 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7180 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7181 G_CALLBACK(compose_grab_focus_cb), compose);
7182 gtk_widget_show(subject_entry);
7183 compose->subject_entry = subject_entry;
7184 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7186 edit_vbox = gtk_vbox_new(FALSE, 0);
7188 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7191 ruler_hbox = gtk_hbox_new(FALSE, 0);
7192 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7194 ruler = gtk_shruler_new();
7195 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7196 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7200 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7201 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7202 GTK_POLICY_AUTOMATIC,
7203 GTK_POLICY_AUTOMATIC);
7204 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7206 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7207 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7209 text = gtk_text_view_new();
7210 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7211 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7212 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7213 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7214 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7216 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7218 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7219 G_CALLBACK(compose_edit_size_alloc),
7221 g_signal_connect(G_OBJECT(buffer), "changed",
7222 G_CALLBACK(compose_changed_cb), compose);
7223 g_signal_connect(G_OBJECT(text), "grab_focus",
7224 G_CALLBACK(compose_grab_focus_cb), compose);
7225 g_signal_connect(G_OBJECT(buffer), "insert_text",
7226 G_CALLBACK(text_inserted), compose);
7227 g_signal_connect(G_OBJECT(text), "button_press_event",
7228 G_CALLBACK(text_clicked), compose);
7230 g_signal_connect(G_OBJECT(text), "popup-menu",
7231 G_CALLBACK(compose_popup_menu), compose);
7233 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7234 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7235 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7236 G_CALLBACK(compose_popup_menu), compose);
7238 g_signal_connect(G_OBJECT(subject_entry), "changed",
7239 G_CALLBACK(compose_changed_cb), compose);
7242 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7243 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7244 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7245 g_signal_connect(G_OBJECT(text), "drag_data_received",
7246 G_CALLBACK(compose_insert_drag_received_cb),
7248 g_signal_connect(G_OBJECT(text), "drag-drop",
7249 G_CALLBACK(compose_drag_drop),
7251 gtk_widget_show_all(vbox);
7253 /* pane between attach clist and text */
7254 paned = gtk_vpaned_new();
7255 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7257 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7258 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7260 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7262 gtk_paned_add1(GTK_PANED(paned), notebook);
7263 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7264 gtk_widget_show_all(paned);
7267 if (prefs_common.textfont) {
7268 PangoFontDescription *font_desc;
7270 font_desc = pango_font_description_from_string
7271 (prefs_common.textfont);
7273 gtk_widget_modify_font(text, font_desc);
7274 pango_font_description_free(font_desc);
7278 gtk_action_group_add_actions(action_group, compose_popup_entries,
7279 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7287 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7289 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7290 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7291 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7293 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7295 undostruct = undo_init(text);
7296 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7299 address_completion_start(window);
7301 compose->window = window;
7302 compose->vbox = vbox;
7303 compose->menubar = menubar;
7304 compose->handlebox = handlebox;
7306 compose->vbox2 = vbox2;
7308 compose->paned = paned;
7310 compose->attach_label = attach_lab2;
7312 compose->notebook = notebook;
7313 compose->edit_vbox = edit_vbox;
7314 compose->ruler_hbox = ruler_hbox;
7315 compose->ruler = ruler;
7316 compose->scrolledwin = scrolledwin;
7317 compose->text = text;
7319 compose->focused_editable = NULL;
7321 compose->popupmenu = popupmenu;
7323 compose->tmpl_menu = tmpl_menu;
7325 compose->mode = mode;
7326 compose->rmode = mode;
7328 compose->targetinfo = NULL;
7329 compose->replyinfo = NULL;
7330 compose->fwdinfo = NULL;
7332 compose->replyto = NULL;
7334 compose->bcc = NULL;
7335 compose->followup_to = NULL;
7337 compose->ml_post = NULL;
7339 compose->inreplyto = NULL;
7340 compose->references = NULL;
7341 compose->msgid = NULL;
7342 compose->boundary = NULL;
7344 compose->autowrap = prefs_common.autowrap;
7345 compose->autoindent = prefs_common.auto_indent;
7346 compose->use_signing = FALSE;
7347 compose->use_encryption = FALSE;
7348 compose->privacy_system = NULL;
7350 compose->modified = FALSE;
7352 compose->return_receipt = FALSE;
7354 compose->to_list = NULL;
7355 compose->newsgroup_list = NULL;
7357 compose->undostruct = undostruct;
7359 compose->sig_str = NULL;
7361 compose->exteditor_file = NULL;
7362 compose->exteditor_pid = -1;
7363 compose->exteditor_tag = -1;
7364 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7367 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7368 if (mode != COMPOSE_REDIRECT) {
7369 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7370 strcmp(prefs_common.dictionary, "")) {
7371 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7372 prefs_common.alt_dictionary,
7373 conv_get_locale_charset_str(),
7374 prefs_common.misspelled_col,
7375 prefs_common.check_while_typing,
7376 prefs_common.recheck_when_changing_dict,
7377 prefs_common.use_alternate,
7378 prefs_common.use_both_dicts,
7379 GTK_TEXT_VIEW(text),
7380 GTK_WINDOW(compose->window),
7381 compose_dict_changed,
7382 compose_spell_menu_changed,
7385 alertpanel_error(_("Spell checker could not "
7387 gtkaspell_checkers_strerror());
7388 gtkaspell_checkers_reset_error();
7390 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7394 compose->gtkaspell = gtkaspell;
7395 compose_spell_menu_changed(compose);
7396 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7399 compose_select_account(compose, account, TRUE);
7401 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7402 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7404 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7405 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7407 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7408 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7410 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7411 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7414 if (account->protocol != A_NNTP)
7415 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7416 prefs_common_translated_header_name("To:"));
7418 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7419 prefs_common_translated_header_name("Newsgroups:"));
7421 addressbook_set_target_compose(compose);
7423 if (mode != COMPOSE_REDIRECT)
7424 compose_set_template_menu(compose);
7426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7429 compose_list = g_list_append(compose_list, compose);
7431 if (!prefs_common.show_ruler)
7432 gtk_widget_hide(ruler_hbox);
7434 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7437 compose->priority = PRIORITY_NORMAL;
7438 compose_update_priority_menu_item(compose);
7440 compose_set_out_encoding(compose);
7443 compose_update_actions_menu(compose);
7445 /* Privacy Systems menu */
7446 compose_update_privacy_systems_menu(compose);
7448 activate_privacy_system(compose, account, TRUE);
7449 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7451 gtk_widget_realize(window);
7453 gtk_widget_show(window);
7455 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7456 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7463 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7468 GtkWidget *optmenubox;
7471 GtkWidget *from_name = NULL;
7472 #if !(GTK_CHECK_VERSION(2,12,0))
7473 GtkTooltips *tips = compose->tooltips;
7476 gint num = 0, def_menu = 0;
7478 accounts = account_get_list();
7479 cm_return_val_if_fail(accounts != NULL, NULL);
7481 optmenubox = gtk_event_box_new();
7482 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7483 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7485 hbox = gtk_hbox_new(FALSE, 6);
7486 from_name = gtk_entry_new();
7488 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7489 G_CALLBACK(compose_grab_focus_cb), compose);
7491 for (; accounts != NULL; accounts = accounts->next, num++) {
7492 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7493 gchar *name, *from = NULL;
7495 if (ac == compose->account) def_menu = num;
7497 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7500 if (ac == compose->account) {
7501 if (ac->name && *ac->name) {
7503 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7504 from = g_strdup_printf("%s <%s>",
7506 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7508 from = g_strdup_printf("%s",
7510 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7513 COMBOBOX_ADD(menu, name, ac->account_id);
7518 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7520 g_signal_connect(G_OBJECT(optmenu), "changed",
7521 G_CALLBACK(account_activated),
7523 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7524 G_CALLBACK(compose_entry_popup_extend),
7527 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7528 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7530 CLAWS_SET_TIP(optmenubox,
7531 _("Account to use for this email"));
7532 CLAWS_SET_TIP(from_name,
7533 _("Sender address to be used"));
7535 compose->from_name = from_name;
7540 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7542 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7543 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7544 Compose *compose = (Compose *) data;
7546 compose->priority = value;
7550 static void compose_reply_change_mode(Compose *compose,
7553 gboolean was_modified = compose->modified;
7555 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7557 cm_return_if_fail(compose->replyinfo != NULL);
7559 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7561 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7563 if (action == COMPOSE_REPLY_TO_ALL)
7565 if (action == COMPOSE_REPLY_TO_SENDER)
7567 if (action == COMPOSE_REPLY_TO_LIST)
7570 compose_remove_header_entries(compose);
7571 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7572 if (compose->account->set_autocc && compose->account->auto_cc)
7573 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7575 if (compose->account->set_autobcc && compose->account->auto_bcc)
7576 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7578 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7579 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7580 compose_show_first_last_header(compose, TRUE);
7581 compose->modified = was_modified;
7582 compose_set_title(compose);
7585 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7587 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7588 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7589 Compose *compose = (Compose *) data;
7592 compose_reply_change_mode(compose, value);
7595 static void compose_update_priority_menu_item(Compose * compose)
7597 GtkWidget *menuitem = NULL;
7598 switch (compose->priority) {
7599 case PRIORITY_HIGHEST:
7600 menuitem = gtk_ui_manager_get_widget
7601 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7604 menuitem = gtk_ui_manager_get_widget
7605 (compose->ui_manager, "/Menu/Options/Priority/High");
7607 case PRIORITY_NORMAL:
7608 menuitem = gtk_ui_manager_get_widget
7609 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7612 menuitem = gtk_ui_manager_get_widget
7613 (compose->ui_manager, "/Menu/Options/Priority/Low");
7615 case PRIORITY_LOWEST:
7616 menuitem = gtk_ui_manager_get_widget
7617 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7620 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7623 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7625 Compose *compose = (Compose *) data;
7627 gboolean can_sign = FALSE, can_encrypt = FALSE;
7629 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7631 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7634 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7635 g_free(compose->privacy_system);
7636 compose->privacy_system = NULL;
7637 if (systemid != NULL) {
7638 compose->privacy_system = g_strdup(systemid);
7640 can_sign = privacy_system_can_sign(systemid);
7641 can_encrypt = privacy_system_can_encrypt(systemid);
7644 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7646 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7647 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7650 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7652 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7653 GtkWidget *menuitem = NULL;
7655 gboolean can_sign = FALSE, can_encrypt = FALSE;
7656 gboolean found = FALSE;
7658 if (compose->privacy_system != NULL) {
7660 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7661 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7662 cm_return_if_fail(menuitem != NULL);
7664 amenu = GTK_MENU_SHELL(menuitem)->children;
7666 while (amenu != NULL) {
7667 GList *alist = amenu->next;
7669 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7670 if (systemid != NULL) {
7671 if (strcmp(systemid, compose->privacy_system) == 0 &&
7672 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7673 menuitem = GTK_WIDGET(amenu->data);
7675 can_sign = privacy_system_can_sign(systemid);
7676 can_encrypt = privacy_system_can_encrypt(systemid);
7680 } else if (strlen(compose->privacy_system) == 0 &&
7681 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7682 menuitem = GTK_WIDGET(amenu->data);
7685 can_encrypt = FALSE;
7692 if (menuitem != NULL)
7693 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7695 if (warn && !found && strlen(compose->privacy_system)) {
7696 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7697 "will not be able to sign or encrypt this message."),
7698 compose->privacy_system);
7702 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7703 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7706 static void compose_set_out_encoding(Compose *compose)
7708 CharSet out_encoding;
7709 const gchar *branch = NULL;
7710 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7712 switch(out_encoding) {
7713 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7714 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7715 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7716 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7717 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7718 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7719 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7720 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7721 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7722 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7723 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7724 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7725 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7726 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7727 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7728 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7729 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7730 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7731 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7732 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7733 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7734 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7735 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7736 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7737 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7738 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7739 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7740 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7741 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7742 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7743 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7744 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7746 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7749 static void compose_set_template_menu(Compose *compose)
7751 GSList *tmpl_list, *cur;
7755 tmpl_list = template_get_config();
7757 menu = gtk_menu_new();
7759 gtk_menu_set_accel_group (GTK_MENU (menu),
7760 gtk_ui_manager_get_accel_group(compose->ui_manager));
7761 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7762 Template *tmpl = (Template *)cur->data;
7763 gchar *accel_path = NULL;
7764 item = gtk_menu_item_new_with_label(tmpl->name);
7765 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7766 g_signal_connect(G_OBJECT(item), "activate",
7767 G_CALLBACK(compose_template_activate_cb),
7769 g_object_set_data(G_OBJECT(item), "template", tmpl);
7770 gtk_widget_show(item);
7771 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7772 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7776 gtk_widget_show(menu);
7777 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7780 void compose_update_actions_menu(Compose *compose)
7782 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7785 static void compose_update_privacy_systems_menu(Compose *compose)
7787 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7788 GSList *systems, *cur;
7790 GtkWidget *system_none;
7792 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7793 GtkWidget *privacy_menu = gtk_menu_new();
7795 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7796 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7798 g_signal_connect(G_OBJECT(system_none), "activate",
7799 G_CALLBACK(compose_set_privacy_system_cb), compose);
7801 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7802 gtk_widget_show(system_none);
7804 systems = privacy_get_system_ids();
7805 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7806 gchar *systemid = cur->data;
7808 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7809 widget = gtk_radio_menu_item_new_with_label(group,
7810 privacy_system_get_name(systemid));
7811 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7812 g_strdup(systemid), g_free);
7813 g_signal_connect(G_OBJECT(widget), "activate",
7814 G_CALLBACK(compose_set_privacy_system_cb), compose);
7816 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7817 gtk_widget_show(widget);
7820 g_slist_free(systems);
7821 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7822 gtk_widget_show_all(privacy_menu);
7823 gtk_widget_show_all(privacy_menuitem);
7826 void compose_reflect_prefs_all(void)
7831 for (cur = compose_list; cur != NULL; cur = cur->next) {
7832 compose = (Compose *)cur->data;
7833 compose_set_template_menu(compose);
7837 void compose_reflect_prefs_pixmap_theme(void)
7842 for (cur = compose_list; cur != NULL; cur = cur->next) {
7843 compose = (Compose *)cur->data;
7844 toolbar_update(TOOLBAR_COMPOSE, compose);
7848 static const gchar *compose_quote_char_from_context(Compose *compose)
7850 const gchar *qmark = NULL;
7852 cm_return_val_if_fail(compose != NULL, NULL);
7854 switch (compose->mode) {
7855 /* use forward-specific quote char */
7856 case COMPOSE_FORWARD:
7857 case COMPOSE_FORWARD_AS_ATTACH:
7858 case COMPOSE_FORWARD_INLINE:
7859 if (compose->folder && compose->folder->prefs &&
7860 compose->folder->prefs->forward_with_format)
7861 qmark = compose->folder->prefs->forward_quotemark;
7862 else if (compose->account->forward_with_format)
7863 qmark = compose->account->forward_quotemark;
7865 qmark = prefs_common.fw_quotemark;
7868 /* use reply-specific quote char in all other modes */
7870 if (compose->folder && compose->folder->prefs &&
7871 compose->folder->prefs->reply_with_format)
7872 qmark = compose->folder->prefs->reply_quotemark;
7873 else if (compose->account->reply_with_format)
7874 qmark = compose->account->reply_quotemark;
7876 qmark = prefs_common.quotemark;
7880 if (qmark == NULL || *qmark == '\0')
7886 static void compose_template_apply(Compose *compose, Template *tmpl,
7890 GtkTextBuffer *buffer;
7894 gchar *parsed_str = NULL;
7895 gint cursor_pos = 0;
7896 const gchar *err_msg = _("Template body format error at line %d.");
7899 /* process the body */
7901 text = GTK_TEXT_VIEW(compose->text);
7902 buffer = gtk_text_view_get_buffer(text);
7905 qmark = compose_quote_char_from_context(compose);
7907 if (compose->replyinfo != NULL) {
7910 gtk_text_buffer_set_text(buffer, "", -1);
7911 mark = gtk_text_buffer_get_insert(buffer);
7912 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7914 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7915 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7917 } else if (compose->fwdinfo != NULL) {
7920 gtk_text_buffer_set_text(buffer, "", -1);
7921 mark = gtk_text_buffer_get_insert(buffer);
7922 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7924 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7925 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7928 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7930 GtkTextIter start, end;
7933 gtk_text_buffer_get_start_iter(buffer, &start);
7934 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7935 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7937 /* clear the buffer now */
7939 gtk_text_buffer_set_text(buffer, "", -1);
7941 parsed_str = compose_quote_fmt(compose, dummyinfo,
7942 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7943 procmsg_msginfo_free( dummyinfo );
7949 gtk_text_buffer_set_text(buffer, "", -1);
7950 mark = gtk_text_buffer_get_insert(buffer);
7951 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7954 if (replace && parsed_str && compose->account->auto_sig)
7955 compose_insert_sig(compose, FALSE);
7957 if (replace && parsed_str) {
7958 gtk_text_buffer_get_start_iter(buffer, &iter);
7959 gtk_text_buffer_place_cursor(buffer, &iter);
7963 cursor_pos = quote_fmt_get_cursor_pos();
7964 compose->set_cursor_pos = cursor_pos;
7965 if (cursor_pos == -1)
7967 gtk_text_buffer_get_start_iter(buffer, &iter);
7968 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7969 gtk_text_buffer_place_cursor(buffer, &iter);
7972 /* process the other fields */
7974 compose_template_apply_fields(compose, tmpl);
7975 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7976 quote_fmt_reset_vartable();
7977 compose_changed_cb(NULL, compose);
7980 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7982 MsgInfo* dummyinfo = NULL;
7983 MsgInfo *msginfo = NULL;
7986 if (compose->replyinfo != NULL)
7987 msginfo = compose->replyinfo;
7988 else if (compose->fwdinfo != NULL)
7989 msginfo = compose->fwdinfo;
7991 dummyinfo = compose_msginfo_new_from_compose(compose);
7992 msginfo = dummyinfo;
7995 if (tmpl->from && *tmpl->from != '\0') {
7997 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7998 compose->gtkaspell);
8000 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8002 quote_fmt_scan_string(tmpl->from);
8005 buf = quote_fmt_get_buffer();
8007 alertpanel_error(_("Template From format error."));
8009 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8013 if (tmpl->to && *tmpl->to != '\0') {
8015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8016 compose->gtkaspell);
8018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8020 quote_fmt_scan_string(tmpl->to);
8023 buf = quote_fmt_get_buffer();
8025 alertpanel_error(_("Template To format error."));
8027 compose_entry_append(compose, buf, COMPOSE_TO);
8031 if (tmpl->cc && *tmpl->cc != '\0') {
8033 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8034 compose->gtkaspell);
8036 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8038 quote_fmt_scan_string(tmpl->cc);
8041 buf = quote_fmt_get_buffer();
8043 alertpanel_error(_("Template Cc format error."));
8045 compose_entry_append(compose, buf, COMPOSE_CC);
8049 if (tmpl->bcc && *tmpl->bcc != '\0') {
8051 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8052 compose->gtkaspell);
8054 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8056 quote_fmt_scan_string(tmpl->bcc);
8059 buf = quote_fmt_get_buffer();
8061 alertpanel_error(_("Template Bcc format error."));
8063 compose_entry_append(compose, buf, COMPOSE_BCC);
8067 /* process the subject */
8068 if (tmpl->subject && *tmpl->subject != '\0') {
8070 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8071 compose->gtkaspell);
8073 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8075 quote_fmt_scan_string(tmpl->subject);
8078 buf = quote_fmt_get_buffer();
8080 alertpanel_error(_("Template subject format error."));
8082 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8086 procmsg_msginfo_free( dummyinfo );
8089 static void compose_destroy(Compose *compose)
8091 GtkTextBuffer *buffer;
8092 GtkClipboard *clipboard;
8094 compose_list = g_list_remove(compose_list, compose);
8096 if (compose->updating) {
8097 debug_print("danger, not destroying anything now\n");
8098 compose->deferred_destroy = TRUE;
8101 /* NOTE: address_completion_end() does nothing with the window
8102 * however this may change. */
8103 address_completion_end(compose->window);
8105 slist_free_strings(compose->to_list);
8106 g_slist_free(compose->to_list);
8107 slist_free_strings(compose->newsgroup_list);
8108 g_slist_free(compose->newsgroup_list);
8109 slist_free_strings(compose->header_list);
8110 g_slist_free(compose->header_list);
8112 procmsg_msginfo_free(compose->targetinfo);
8113 procmsg_msginfo_free(compose->replyinfo);
8114 procmsg_msginfo_free(compose->fwdinfo);
8116 g_free(compose->replyto);
8117 g_free(compose->cc);
8118 g_free(compose->bcc);
8119 g_free(compose->newsgroups);
8120 g_free(compose->followup_to);
8122 g_free(compose->ml_post);
8124 g_free(compose->inreplyto);
8125 g_free(compose->references);
8126 g_free(compose->msgid);
8127 g_free(compose->boundary);
8129 g_free(compose->redirect_filename);
8130 if (compose->undostruct)
8131 undo_destroy(compose->undostruct);
8133 g_free(compose->sig_str);
8135 g_free(compose->exteditor_file);
8137 g_free(compose->orig_charset);
8139 g_free(compose->privacy_system);
8141 if (addressbook_get_target_compose() == compose)
8142 addressbook_set_target_compose(NULL);
8145 if (compose->gtkaspell) {
8146 gtkaspell_delete(compose->gtkaspell);
8147 compose->gtkaspell = NULL;
8151 if (!compose->batch) {
8152 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8153 prefs_common.compose_height = compose->window->allocation.height;
8156 if (!gtk_widget_get_parent(compose->paned))
8157 gtk_widget_destroy(compose->paned);
8158 gtk_widget_destroy(compose->popupmenu);
8160 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8161 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8162 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8164 gtk_widget_destroy(compose->window);
8165 toolbar_destroy(compose->toolbar);
8166 g_free(compose->toolbar);
8167 g_mutex_free(compose->mutex);
8171 static void compose_attach_info_free(AttachInfo *ainfo)
8173 g_free(ainfo->file);
8174 g_free(ainfo->content_type);
8175 g_free(ainfo->name);
8179 static void compose_attach_update_label(Compose *compose)
8184 GtkTreeModel *model;
8189 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8190 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8191 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8195 while(gtk_tree_model_iter_next(model, &iter))
8198 text = g_strdup_printf("(%d)", i);
8199 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8203 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8205 Compose *compose = (Compose *)data;
8206 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8207 GtkTreeSelection *selection;
8209 GtkTreeModel *model;
8211 selection = gtk_tree_view_get_selection(tree_view);
8212 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8217 for (cur = sel; cur != NULL; cur = cur->next) {
8218 GtkTreePath *path = cur->data;
8219 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8222 gtk_tree_path_free(path);
8225 for (cur = sel; cur != NULL; cur = cur->next) {
8226 GtkTreeRowReference *ref = cur->data;
8227 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8230 if (gtk_tree_model_get_iter(model, &iter, path))
8231 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8233 gtk_tree_path_free(path);
8234 gtk_tree_row_reference_free(ref);
8238 compose_attach_update_label(compose);
8241 static struct _AttachProperty
8244 GtkWidget *mimetype_entry;
8245 GtkWidget *encoding_optmenu;
8246 GtkWidget *path_entry;
8247 GtkWidget *filename_entry;
8249 GtkWidget *cancel_btn;
8252 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8254 gtk_tree_path_free((GtkTreePath *)ptr);
8257 static void compose_attach_property(GtkAction *action, gpointer data)
8259 Compose *compose = (Compose *)data;
8260 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8262 GtkComboBox *optmenu;
8263 GtkTreeSelection *selection;
8265 GtkTreeModel *model;
8268 static gboolean cancelled;
8270 /* only if one selected */
8271 selection = gtk_tree_view_get_selection(tree_view);
8272 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8275 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8279 path = (GtkTreePath *) sel->data;
8280 gtk_tree_model_get_iter(model, &iter, path);
8281 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8284 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8290 if (!attach_prop.window)
8291 compose_attach_property_create(&cancelled);
8292 gtk_widget_grab_focus(attach_prop.ok_btn);
8293 gtk_widget_show(attach_prop.window);
8294 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8296 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8297 if (ainfo->encoding == ENC_UNKNOWN)
8298 combobox_select_by_data(optmenu, ENC_BASE64);
8300 combobox_select_by_data(optmenu, ainfo->encoding);
8302 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8303 ainfo->content_type ? ainfo->content_type : "");
8304 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8305 ainfo->file ? ainfo->file : "");
8306 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8307 ainfo->name ? ainfo->name : "");
8310 const gchar *entry_text;
8312 gchar *cnttype = NULL;
8319 gtk_widget_hide(attach_prop.window);
8324 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8325 if (*entry_text != '\0') {
8328 text = g_strstrip(g_strdup(entry_text));
8329 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8330 cnttype = g_strdup(text);
8333 alertpanel_error(_("Invalid MIME type."));
8339 ainfo->encoding = combobox_get_active_data(optmenu);
8341 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8342 if (*entry_text != '\0') {
8343 if (is_file_exist(entry_text) &&
8344 (size = get_file_size(entry_text)) > 0)
8345 file = g_strdup(entry_text);
8348 (_("File doesn't exist or is empty."));
8354 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8355 if (*entry_text != '\0') {
8356 g_free(ainfo->name);
8357 ainfo->name = g_strdup(entry_text);
8361 g_free(ainfo->content_type);
8362 ainfo->content_type = cnttype;
8365 g_free(ainfo->file);
8369 ainfo->size = (goffset)size;
8371 /* update tree store */
8372 text = to_human_readable(ainfo->size);
8373 gtk_tree_model_get_iter(model, &iter, path);
8374 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8375 COL_MIMETYPE, ainfo->content_type,
8377 COL_NAME, ainfo->name,
8383 gtk_tree_path_free(path);
8386 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8388 label = gtk_label_new(str); \
8389 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8390 GTK_FILL, 0, 0, 0); \
8391 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8393 entry = gtk_entry_new(); \
8394 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8395 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8398 static void compose_attach_property_create(gboolean *cancelled)
8404 GtkWidget *mimetype_entry;
8407 GtkListStore *optmenu_menu;
8408 GtkWidget *path_entry;
8409 GtkWidget *filename_entry;
8412 GtkWidget *cancel_btn;
8413 GList *mime_type_list, *strlist;
8416 debug_print("Creating attach_property window...\n");
8418 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8419 gtk_widget_set_size_request(window, 480, -1);
8420 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8421 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8422 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8423 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8424 g_signal_connect(G_OBJECT(window), "delete_event",
8425 G_CALLBACK(attach_property_delete_event),
8427 g_signal_connect(G_OBJECT(window), "key_press_event",
8428 G_CALLBACK(attach_property_key_pressed),
8431 vbox = gtk_vbox_new(FALSE, 8);
8432 gtk_container_add(GTK_CONTAINER(window), vbox);
8434 table = gtk_table_new(4, 2, FALSE);
8435 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8436 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8437 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8439 label = gtk_label_new(_("MIME type"));
8440 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8442 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8443 mimetype_entry = gtk_combo_box_entry_new_text();
8444 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8445 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8447 /* stuff with list */
8448 mime_type_list = procmime_get_mime_type_list();
8450 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8451 MimeType *type = (MimeType *) mime_type_list->data;
8454 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8456 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8459 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8460 (GCompareFunc)strcmp2);
8463 for (mime_type_list = strlist; mime_type_list != NULL;
8464 mime_type_list = mime_type_list->next) {
8465 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8466 g_free(mime_type_list->data);
8468 g_list_free(strlist);
8469 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8470 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8472 label = gtk_label_new(_("Encoding"));
8473 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8475 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8477 hbox = gtk_hbox_new(FALSE, 0);
8478 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8479 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8481 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8482 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8484 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8485 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8486 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8487 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8488 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8490 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8492 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8493 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8495 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8496 &ok_btn, GTK_STOCK_OK,
8498 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8499 gtk_widget_grab_default(ok_btn);
8501 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8502 G_CALLBACK(attach_property_ok),
8504 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8505 G_CALLBACK(attach_property_cancel),
8508 gtk_widget_show_all(vbox);
8510 attach_prop.window = window;
8511 attach_prop.mimetype_entry = mimetype_entry;
8512 attach_prop.encoding_optmenu = optmenu;
8513 attach_prop.path_entry = path_entry;
8514 attach_prop.filename_entry = filename_entry;
8515 attach_prop.ok_btn = ok_btn;
8516 attach_prop.cancel_btn = cancel_btn;
8519 #undef SET_LABEL_AND_ENTRY
8521 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8527 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8533 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8534 gboolean *cancelled)
8542 static gboolean attach_property_key_pressed(GtkWidget *widget,
8544 gboolean *cancelled)
8546 if (event && event->keyval == GDK_Escape) {
8550 if (event && event->keyval == GDK_Return) {
8557 static void compose_exec_ext_editor(Compose *compose)
8564 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8565 G_DIR_SEPARATOR, compose);
8567 if (pipe(pipe_fds) < 0) {
8573 if ((pid = fork()) < 0) {
8580 /* close the write side of the pipe */
8583 compose->exteditor_file = g_strdup(tmp);
8584 compose->exteditor_pid = pid;
8586 compose_set_ext_editor_sensitive(compose, FALSE);
8589 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8591 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8593 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8597 } else { /* process-monitoring process */
8603 /* close the read side of the pipe */
8606 if (compose_write_body_to_file(compose, tmp) < 0) {
8607 fd_write_all(pipe_fds[1], "2\n", 2);
8611 pid_ed = compose_exec_ext_editor_real(tmp);
8613 fd_write_all(pipe_fds[1], "1\n", 2);
8617 /* wait until editor is terminated */
8618 waitpid(pid_ed, NULL, 0);
8620 fd_write_all(pipe_fds[1], "0\n", 2);
8627 #endif /* G_OS_UNIX */
8631 static gint compose_exec_ext_editor_real(const gchar *file)
8638 cm_return_val_if_fail(file != NULL, -1);
8640 if ((pid = fork()) < 0) {
8645 if (pid != 0) return pid;
8647 /* grandchild process */
8649 if (setpgid(0, getppid()))
8652 if (prefs_common_get_ext_editor_cmd() &&
8653 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8654 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8655 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8657 if (prefs_common_get_ext_editor_cmd())
8658 g_warning("External editor command-line is invalid: '%s'\n",
8659 prefs_common_get_ext_editor_cmd());
8660 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8663 cmdline = strsplit_with_quote(buf, " ", 1024);
8664 execvp(cmdline[0], cmdline);
8667 g_strfreev(cmdline);
8672 static gboolean compose_ext_editor_kill(Compose *compose)
8674 pid_t pgid = compose->exteditor_pid * -1;
8677 ret = kill(pgid, 0);
8679 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8683 msg = g_strdup_printf
8684 (_("The external editor is still working.\n"
8685 "Force terminating the process?\n"
8686 "process group id: %d"), -pgid);
8687 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8688 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8692 if (val == G_ALERTALTERNATE) {
8693 g_source_remove(compose->exteditor_tag);
8694 g_io_channel_shutdown(compose->exteditor_ch,
8696 g_io_channel_unref(compose->exteditor_ch);
8698 if (kill(pgid, SIGTERM) < 0) perror("kill");
8699 waitpid(compose->exteditor_pid, NULL, 0);
8701 g_warning("Terminated process group id: %d", -pgid);
8702 g_warning("Temporary file: %s",
8703 compose->exteditor_file);
8705 compose_set_ext_editor_sensitive(compose, TRUE);
8707 g_free(compose->exteditor_file);
8708 compose->exteditor_file = NULL;
8709 compose->exteditor_pid = -1;
8710 compose->exteditor_ch = NULL;
8711 compose->exteditor_tag = -1;
8719 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8723 Compose *compose = (Compose *)data;
8726 debug_print(_("Compose: input from monitoring process\n"));
8728 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8730 g_io_channel_shutdown(source, FALSE, NULL);
8731 g_io_channel_unref(source);
8733 waitpid(compose->exteditor_pid, NULL, 0);
8735 if (buf[0] == '0') { /* success */
8736 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8737 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8739 gtk_text_buffer_set_text(buffer, "", -1);
8740 compose_insert_file(compose, compose->exteditor_file);
8741 compose_changed_cb(NULL, compose);
8742 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8744 if (claws_unlink(compose->exteditor_file) < 0)
8745 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8746 } else if (buf[0] == '1') { /* failed */
8747 g_warning("Couldn't exec external editor\n");
8748 if (claws_unlink(compose->exteditor_file) < 0)
8749 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8750 } else if (buf[0] == '2') {
8751 g_warning("Couldn't write to file\n");
8752 } else if (buf[0] == '3') {
8753 g_warning("Pipe read failed\n");
8756 compose_set_ext_editor_sensitive(compose, TRUE);
8758 g_free(compose->exteditor_file);
8759 compose->exteditor_file = NULL;
8760 compose->exteditor_pid = -1;
8761 compose->exteditor_ch = NULL;
8762 compose->exteditor_tag = -1;
8767 static void compose_set_ext_editor_sensitive(Compose *compose,
8770 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8771 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8772 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8773 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8774 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8775 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8776 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8778 gtk_widget_set_sensitive(compose->text, sensitive);
8779 if (compose->toolbar->send_btn)
8780 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8781 if (compose->toolbar->sendl_btn)
8782 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8783 if (compose->toolbar->draft_btn)
8784 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8785 if (compose->toolbar->insert_btn)
8786 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8787 if (compose->toolbar->sig_btn)
8788 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8789 if (compose->toolbar->exteditor_btn)
8790 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8791 if (compose->toolbar->linewrap_current_btn)
8792 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8793 if (compose->toolbar->linewrap_all_btn)
8794 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8796 #endif /* G_OS_UNIX */
8799 * compose_undo_state_changed:
8801 * Change the sensivity of the menuentries undo and redo
8803 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8804 gint redo_state, gpointer data)
8806 Compose *compose = (Compose *)data;
8808 switch (undo_state) {
8809 case UNDO_STATE_TRUE:
8810 if (!undostruct->undo_state) {
8811 undostruct->undo_state = TRUE;
8812 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8815 case UNDO_STATE_FALSE:
8816 if (undostruct->undo_state) {
8817 undostruct->undo_state = FALSE;
8818 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8821 case UNDO_STATE_UNCHANGED:
8823 case UNDO_STATE_REFRESH:
8824 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8827 g_warning("Undo state not recognized");
8831 switch (redo_state) {
8832 case UNDO_STATE_TRUE:
8833 if (!undostruct->redo_state) {
8834 undostruct->redo_state = TRUE;
8835 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8838 case UNDO_STATE_FALSE:
8839 if (undostruct->redo_state) {
8840 undostruct->redo_state = FALSE;
8841 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8844 case UNDO_STATE_UNCHANGED:
8846 case UNDO_STATE_REFRESH:
8847 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8850 g_warning("Redo state not recognized");
8855 /* callback functions */
8857 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8858 * includes "non-client" (windows-izm) in calculation, so this calculation
8859 * may not be accurate.
8861 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8862 GtkAllocation *allocation,
8863 GtkSHRuler *shruler)
8865 if (prefs_common.show_ruler) {
8866 gint char_width = 0, char_height = 0;
8867 gint line_width_in_chars;
8869 gtkut_get_font_size(GTK_WIDGET(widget),
8870 &char_width, &char_height);
8871 line_width_in_chars =
8872 (allocation->width - allocation->x) / char_width;
8874 /* got the maximum */
8875 gtk_ruler_set_range(GTK_RULER(shruler),
8876 0.0, line_width_in_chars, 0,
8877 /*line_width_in_chars*/ char_width);
8883 static void account_activated(GtkComboBox *optmenu, gpointer data)
8885 Compose *compose = (Compose *)data;
8888 gchar *folderidentifier;
8889 gint account_id = 0;
8893 /* Get ID of active account in the combo box */
8894 menu = gtk_combo_box_get_model(optmenu);
8895 gtk_combo_box_get_active_iter(optmenu, &iter);
8896 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8898 ac = account_find_from_id(account_id);
8899 cm_return_if_fail(ac != NULL);
8901 if (ac != compose->account)
8902 compose_select_account(compose, ac, FALSE);
8904 /* Set message save folder */
8905 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8906 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8908 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8909 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8911 compose_set_save_to(compose, NULL);
8912 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8913 folderidentifier = folder_item_get_identifier(account_get_special_folder
8914 (compose->account, F_OUTBOX));
8915 compose_set_save_to(compose, folderidentifier);
8916 g_free(folderidentifier);
8920 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8921 GtkTreeViewColumn *column, Compose *compose)
8923 compose_attach_property(NULL, compose);
8926 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8929 Compose *compose = (Compose *)data;
8930 GtkTreeSelection *attach_selection;
8931 gint attach_nr_selected;
8933 if (!event) return FALSE;
8935 if (event->button == 3) {
8936 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8937 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8939 if (attach_nr_selected > 0)
8941 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8942 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8944 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8945 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8948 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8949 NULL, NULL, event->button, event->time);
8956 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8959 Compose *compose = (Compose *)data;
8961 if (!event) return FALSE;
8963 switch (event->keyval) {
8965 compose_attach_remove_selected(NULL, compose);
8971 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8973 toolbar_comp_set_sensitive(compose, allow);
8974 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8975 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8977 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8979 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8980 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8981 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8983 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8987 static void compose_send_cb(GtkAction *action, gpointer data)
8989 Compose *compose = (Compose *)data;
8991 if (prefs_common.work_offline &&
8992 !inc_offline_should_override(TRUE,
8993 _("Claws Mail needs network access in order "
8994 "to send this email.")))
8997 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8998 g_source_remove(compose->draft_timeout_tag);
8999 compose->draft_timeout_tag = -1;
9002 compose_send(compose);
9005 static void compose_send_later_cb(GtkAction *action, gpointer data)
9007 Compose *compose = (Compose *)data;
9011 compose_allow_user_actions(compose, FALSE);
9012 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9013 compose_allow_user_actions(compose, TRUE);
9017 compose_close(compose);
9018 } else if (val == -1) {
9019 alertpanel_error(_("Could not queue message."));
9020 } else if (val == -2) {
9021 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9022 } else if (val == -3) {
9023 if (privacy_peek_error())
9024 alertpanel_error(_("Could not queue message for sending:\n\n"
9025 "Signature failed: %s"), privacy_get_error());
9026 } else if (val == -4) {
9027 alertpanel_error(_("Could not queue message for sending:\n\n"
9028 "Charset conversion failed."));
9029 } else if (val == -5) {
9030 alertpanel_error(_("Could not queue message for sending:\n\n"
9031 "Couldn't get recipient encryption key."));
9032 } else if (val == -6) {
9035 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9038 #define DRAFTED_AT_EXIT "drafted_at_exit"
9039 static void compose_register_draft(MsgInfo *info)
9041 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9042 DRAFTED_AT_EXIT, NULL);
9043 FILE *fp = g_fopen(filepath, "ab");
9046 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9054 gboolean compose_draft (gpointer data, guint action)
9056 Compose *compose = (Compose *)data;
9060 MsgFlags flag = {0, 0};
9061 static gboolean lock = FALSE;
9062 MsgInfo *newmsginfo;
9064 gboolean target_locked = FALSE;
9065 gboolean err = FALSE;
9067 if (lock) return FALSE;
9069 if (compose->sending)
9072 draft = account_get_special_folder(compose->account, F_DRAFT);
9073 cm_return_val_if_fail(draft != NULL, FALSE);
9075 if (!g_mutex_trylock(compose->mutex)) {
9076 /* we don't want to lock the mutex once it's available,
9077 * because as the only other part of compose.c locking
9078 * it is compose_close - which means once unlocked,
9079 * the compose struct will be freed */
9080 debug_print("couldn't lock mutex, probably sending\n");
9086 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9087 G_DIR_SEPARATOR, compose);
9088 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9089 FILE_OP_ERROR(tmp, "fopen");
9093 /* chmod for security */
9094 if (change_file_mode_rw(fp, tmp) < 0) {
9095 FILE_OP_ERROR(tmp, "chmod");
9096 g_warning("can't change file mode\n");
9099 /* Save draft infos */
9100 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9101 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9103 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9104 gchar *savefolderid;
9106 savefolderid = compose_get_save_to(compose);
9107 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9108 g_free(savefolderid);
9110 if (compose->return_receipt) {
9111 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9113 if (compose->privacy_system) {
9114 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9115 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9116 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9119 /* Message-ID of message replying to */
9120 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9123 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9124 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9127 /* Message-ID of message forwarding to */
9128 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9131 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9132 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9136 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9137 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9139 /* end of headers */
9140 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9147 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9151 if (fclose(fp) == EOF) {
9155 if (compose->targetinfo) {
9156 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9157 flag.perm_flags = target_locked?MSG_LOCKED:0;
9159 flag.tmp_flags = MSG_DRAFT;
9161 folder_item_scan(draft);
9162 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9163 MsgInfo *tmpinfo = NULL;
9164 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9165 if (compose->msgid) {
9166 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9169 msgnum = tmpinfo->msgnum;
9170 procmsg_msginfo_free(tmpinfo);
9171 debug_print("got draft msgnum %d from scanning\n", msgnum);
9173 debug_print("didn't get draft msgnum after scanning\n");
9176 debug_print("got draft msgnum %d from adding\n", msgnum);
9182 if (action != COMPOSE_AUTO_SAVE) {
9183 if (action != COMPOSE_DRAFT_FOR_EXIT)
9184 alertpanel_error(_("Could not save draft."));
9187 gtkut_window_popup(compose->window);
9188 val = alertpanel_full(_("Could not save draft"),
9189 _("Could not save draft.\n"
9190 "Do you want to cancel exit or discard this email?"),
9191 _("_Cancel exit"), _("_Discard email"), NULL,
9192 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9193 if (val == G_ALERTALTERNATE) {
9195 g_mutex_unlock(compose->mutex); /* must be done before closing */
9196 compose_close(compose);
9200 g_mutex_unlock(compose->mutex); /* must be done before closing */
9209 if (compose->mode == COMPOSE_REEDIT) {
9210 compose_remove_reedit_target(compose, TRUE);
9213 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9216 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9218 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9220 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9221 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9222 procmsg_msginfo_set_flags(newmsginfo, 0,
9223 MSG_HAS_ATTACHMENT);
9225 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9226 compose_register_draft(newmsginfo);
9228 procmsg_msginfo_free(newmsginfo);
9231 folder_item_scan(draft);
9233 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9235 g_mutex_unlock(compose->mutex); /* must be done before closing */
9236 compose_close(compose);
9242 path = folder_item_fetch_msg(draft, msgnum);
9244 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9247 if (g_stat(path, &s) < 0) {
9248 FILE_OP_ERROR(path, "stat");
9254 procmsg_msginfo_free(compose->targetinfo);
9255 compose->targetinfo = procmsg_msginfo_new();
9256 compose->targetinfo->msgnum = msgnum;
9257 compose->targetinfo->size = (goffset)s.st_size;
9258 compose->targetinfo->mtime = s.st_mtime;
9259 compose->targetinfo->folder = draft;
9261 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9262 compose->mode = COMPOSE_REEDIT;
9264 if (action == COMPOSE_AUTO_SAVE) {
9265 compose->autosaved_draft = compose->targetinfo;
9267 compose->modified = FALSE;
9268 compose_set_title(compose);
9272 g_mutex_unlock(compose->mutex);
9276 void compose_clear_exit_drafts(void)
9278 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9279 DRAFTED_AT_EXIT, NULL);
9280 if (is_file_exist(filepath))
9281 claws_unlink(filepath);
9286 void compose_reopen_exit_drafts(void)
9288 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9289 DRAFTED_AT_EXIT, NULL);
9290 FILE *fp = g_fopen(filepath, "rb");
9294 while (fgets(buf, sizeof(buf), fp)) {
9295 gchar **parts = g_strsplit(buf, "\t", 2);
9296 const gchar *folder = parts[0];
9297 int msgnum = parts[1] ? atoi(parts[1]):-1;
9299 if (folder && *folder && msgnum > -1) {
9300 FolderItem *item = folder_find_item_from_identifier(folder);
9301 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9303 compose_reedit(info, FALSE);
9310 compose_clear_exit_drafts();
9313 static void compose_save_cb(GtkAction *action, gpointer data)
9315 Compose *compose = (Compose *)data;
9316 compose_draft(compose, COMPOSE_KEEP_EDITING);
9317 compose->rmode = COMPOSE_REEDIT;
9320 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9322 if (compose && file_list) {
9325 for ( tmp = file_list; tmp; tmp = tmp->next) {
9326 gchar *file = (gchar *) tmp->data;
9327 gchar *utf8_filename = conv_filename_to_utf8(file);
9328 compose_attach_append(compose, file, utf8_filename, NULL);
9329 compose_changed_cb(NULL, compose);
9334 g_free(utf8_filename);
9339 static void compose_attach_cb(GtkAction *action, gpointer data)
9341 Compose *compose = (Compose *)data;
9344 if (compose->redirect_filename != NULL)
9347 file_list = filesel_select_multiple_files_open(_("Select file"));
9350 compose_attach_from_list(compose, file_list, TRUE);
9351 g_list_free(file_list);
9355 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9357 Compose *compose = (Compose *)data;
9360 file_list = filesel_select_multiple_files_open(_("Select file"));
9365 for ( tmp = file_list; tmp; tmp = tmp->next) {
9366 gchar *file = (gchar *) tmp->data;
9367 gchar *filedup = g_strdup(file);
9368 gchar *shortfile = g_path_get_basename(filedup);
9369 ComposeInsertResult res;
9371 res = compose_insert_file(compose, file);
9372 if (res == COMPOSE_INSERT_READ_ERROR) {
9373 alertpanel_error(_("File '%s' could not be read."), shortfile);
9374 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9375 alertpanel_error(_("File '%s' contained invalid characters\n"
9376 "for the current encoding, insertion may be incorrect."), shortfile);
9382 g_list_free(file_list);
9386 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9388 Compose *compose = (Compose *)data;
9390 compose_insert_sig(compose, FALSE);
9393 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9397 Compose *compose = (Compose *)data;
9399 gtkut_widget_get_uposition(widget, &x, &y);
9400 if (!compose->batch) {
9401 prefs_common.compose_x = x;
9402 prefs_common.compose_y = y;
9404 if (compose->sending || compose->updating)
9406 compose_close_cb(NULL, compose);
9410 void compose_close_toolbar(Compose *compose)
9412 compose_close_cb(NULL, compose);
9415 static void compose_close_cb(GtkAction *action, gpointer data)
9417 Compose *compose = (Compose *)data;
9421 if (compose->exteditor_tag != -1) {
9422 if (!compose_ext_editor_kill(compose))
9427 if (compose->modified) {
9428 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9429 if (!g_mutex_trylock(compose->mutex)) {
9430 /* we don't want to lock the mutex once it's available,
9431 * because as the only other part of compose.c locking
9432 * it is compose_close - which means once unlocked,
9433 * the compose struct will be freed */
9434 debug_print("couldn't lock mutex, probably sending\n");
9438 val = alertpanel(_("Discard message"),
9439 _("This message has been modified. Discard it?"),
9440 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9442 val = alertpanel(_("Save changes"),
9443 _("This message has been modified. Save the latest changes?"),
9444 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9446 g_mutex_unlock(compose->mutex);
9448 case G_ALERTDEFAULT:
9449 if (prefs_common.autosave && !reedit)
9450 compose_remove_draft(compose);
9452 case G_ALERTALTERNATE:
9453 compose_draft(data, COMPOSE_QUIT_EDITING);
9460 compose_close(compose);
9463 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9465 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9466 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9467 Compose *compose = (Compose *) data;
9470 compose->out_encoding = (CharSet)value;
9473 static void compose_address_cb(GtkAction *action, gpointer data)
9475 Compose *compose = (Compose *)data;
9477 addressbook_open(compose);
9480 static void about_show_cb(GtkAction *action, gpointer data)
9485 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9487 Compose *compose = (Compose *)data;
9492 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9493 cm_return_if_fail(tmpl != NULL);
9495 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9497 val = alertpanel(_("Apply template"), msg,
9498 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9501 if (val == G_ALERTDEFAULT)
9502 compose_template_apply(compose, tmpl, TRUE);
9503 else if (val == G_ALERTALTERNATE)
9504 compose_template_apply(compose, tmpl, FALSE);
9507 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9509 Compose *compose = (Compose *)data;
9511 compose_exec_ext_editor(compose);
9514 static void compose_undo_cb(GtkAction *action, gpointer data)
9516 Compose *compose = (Compose *)data;
9517 gboolean prev_autowrap = compose->autowrap;
9519 compose->autowrap = FALSE;
9520 undo_undo(compose->undostruct);
9521 compose->autowrap = prev_autowrap;
9524 static void compose_redo_cb(GtkAction *action, gpointer data)
9526 Compose *compose = (Compose *)data;
9527 gboolean prev_autowrap = compose->autowrap;
9529 compose->autowrap = FALSE;
9530 undo_redo(compose->undostruct);
9531 compose->autowrap = prev_autowrap;
9534 static void entry_cut_clipboard(GtkWidget *entry)
9536 if (GTK_IS_EDITABLE(entry))
9537 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9538 else if (GTK_IS_TEXT_VIEW(entry))
9539 gtk_text_buffer_cut_clipboard(
9540 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9541 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9545 static void entry_copy_clipboard(GtkWidget *entry)
9547 if (GTK_IS_EDITABLE(entry))
9548 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9549 else if (GTK_IS_TEXT_VIEW(entry))
9550 gtk_text_buffer_copy_clipboard(
9551 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9552 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9555 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9556 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9558 if (GTK_IS_TEXT_VIEW(entry)) {
9559 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9560 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9561 GtkTextIter start_iter, end_iter;
9563 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9565 if (contents == NULL)
9568 /* we shouldn't delete the selection when middle-click-pasting, or we
9569 * can't mid-click-paste our own selection */
9570 if (clip != GDK_SELECTION_PRIMARY) {
9571 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9572 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9575 if (insert_place == NULL) {
9576 /* if insert_place isn't specified, insert at the cursor.
9577 * used for Ctrl-V pasting */
9578 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9579 start = gtk_text_iter_get_offset(&start_iter);
9580 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9582 /* if insert_place is specified, paste here.
9583 * used for mid-click-pasting */
9584 start = gtk_text_iter_get_offset(insert_place);
9585 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9586 if (prefs_common.primary_paste_unselects)
9587 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9591 /* paste unwrapped: mark the paste so it's not wrapped later */
9592 end = start + strlen(contents);
9593 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9594 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9595 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9596 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9597 /* rewrap paragraph now (after a mid-click-paste) */
9598 mark_start = gtk_text_buffer_get_insert(buffer);
9599 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9600 gtk_text_iter_backward_char(&start_iter);
9601 compose_beautify_paragraph(compose, &start_iter, TRUE);
9603 } else if (GTK_IS_EDITABLE(entry))
9604 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9606 compose->modified = TRUE;
9609 static void entry_allsel(GtkWidget *entry)
9611 if (GTK_IS_EDITABLE(entry))
9612 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9613 else if (GTK_IS_TEXT_VIEW(entry)) {
9614 GtkTextIter startiter, enditer;
9615 GtkTextBuffer *textbuf;
9617 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9618 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9619 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9621 gtk_text_buffer_move_mark_by_name(textbuf,
9622 "selection_bound", &startiter);
9623 gtk_text_buffer_move_mark_by_name(textbuf,
9624 "insert", &enditer);
9628 static void compose_cut_cb(GtkAction *action, gpointer data)
9630 Compose *compose = (Compose *)data;
9631 if (compose->focused_editable
9632 #ifndef GENERIC_UMPC
9633 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9636 entry_cut_clipboard(compose->focused_editable);
9639 static void compose_copy_cb(GtkAction *action, gpointer data)
9641 Compose *compose = (Compose *)data;
9642 if (compose->focused_editable
9643 #ifndef GENERIC_UMPC
9644 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9647 entry_copy_clipboard(compose->focused_editable);
9650 static void compose_paste_cb(GtkAction *action, gpointer data)
9652 Compose *compose = (Compose *)data;
9654 GtkTextBuffer *buffer;
9656 if (compose->focused_editable &&
9657 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9658 entry_paste_clipboard(compose, compose->focused_editable,
9659 prefs_common.linewrap_pastes,
9660 GDK_SELECTION_CLIPBOARD, NULL);
9664 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9666 Compose *compose = (Compose *)data;
9667 gint wrap_quote = prefs_common.linewrap_quote;
9668 if (compose->focused_editable
9669 #ifndef GENERIC_UMPC
9670 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9673 /* let text_insert() (called directly or at a later time
9674 * after the gtk_editable_paste_clipboard) know that
9675 * text is to be inserted as a quotation. implemented
9676 * by using a simple refcount... */
9677 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9678 G_OBJECT(compose->focused_editable),
9679 "paste_as_quotation"));
9680 g_object_set_data(G_OBJECT(compose->focused_editable),
9681 "paste_as_quotation",
9682 GINT_TO_POINTER(paste_as_quotation + 1));
9683 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9684 entry_paste_clipboard(compose, compose->focused_editable,
9685 prefs_common.linewrap_pastes,
9686 GDK_SELECTION_CLIPBOARD, NULL);
9687 prefs_common.linewrap_quote = wrap_quote;
9691 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9693 Compose *compose = (Compose *)data;
9695 GtkTextBuffer *buffer;
9697 if (compose->focused_editable
9698 #ifndef GENERIC_UMPC
9699 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9702 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9703 GDK_SELECTION_CLIPBOARD, NULL);
9707 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9709 Compose *compose = (Compose *)data;
9711 GtkTextBuffer *buffer;
9713 if (compose->focused_editable
9714 #ifndef GENERIC_UMPC
9715 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9718 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9719 GDK_SELECTION_CLIPBOARD, NULL);
9723 static void compose_allsel_cb(GtkAction *action, gpointer data)
9725 Compose *compose = (Compose *)data;
9726 if (compose->focused_editable
9727 #ifndef GENERIC_UMPC
9728 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9731 entry_allsel(compose->focused_editable);
9734 static void textview_move_beginning_of_line (GtkTextView *text)
9736 GtkTextBuffer *buffer;
9740 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9742 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9743 mark = gtk_text_buffer_get_insert(buffer);
9744 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9745 gtk_text_iter_set_line_offset(&ins, 0);
9746 gtk_text_buffer_place_cursor(buffer, &ins);
9749 static void textview_move_forward_character (GtkTextView *text)
9751 GtkTextBuffer *buffer;
9755 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9757 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9758 mark = gtk_text_buffer_get_insert(buffer);
9759 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9760 if (gtk_text_iter_forward_cursor_position(&ins))
9761 gtk_text_buffer_place_cursor(buffer, &ins);
9764 static void textview_move_backward_character (GtkTextView *text)
9766 GtkTextBuffer *buffer;
9770 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9772 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9773 mark = gtk_text_buffer_get_insert(buffer);
9774 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9775 if (gtk_text_iter_backward_cursor_position(&ins))
9776 gtk_text_buffer_place_cursor(buffer, &ins);
9779 static void textview_move_forward_word (GtkTextView *text)
9781 GtkTextBuffer *buffer;
9786 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9788 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9789 mark = gtk_text_buffer_get_insert(buffer);
9790 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9791 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9792 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9793 gtk_text_iter_backward_word_start(&ins);
9794 gtk_text_buffer_place_cursor(buffer, &ins);
9798 static void textview_move_backward_word (GtkTextView *text)
9800 GtkTextBuffer *buffer;
9805 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9807 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9808 mark = gtk_text_buffer_get_insert(buffer);
9809 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9810 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9811 if (gtk_text_iter_backward_word_starts(&ins, 1))
9812 gtk_text_buffer_place_cursor(buffer, &ins);
9815 static void textview_move_end_of_line (GtkTextView *text)
9817 GtkTextBuffer *buffer;
9821 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9823 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9824 mark = gtk_text_buffer_get_insert(buffer);
9825 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9826 if (gtk_text_iter_forward_to_line_end(&ins))
9827 gtk_text_buffer_place_cursor(buffer, &ins);
9830 static void textview_move_next_line (GtkTextView *text)
9832 GtkTextBuffer *buffer;
9837 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9839 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9840 mark = gtk_text_buffer_get_insert(buffer);
9841 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9842 offset = gtk_text_iter_get_line_offset(&ins);
9843 if (gtk_text_iter_forward_line(&ins)) {
9844 gtk_text_iter_set_line_offset(&ins, offset);
9845 gtk_text_buffer_place_cursor(buffer, &ins);
9849 static void textview_move_previous_line (GtkTextView *text)
9851 GtkTextBuffer *buffer;
9856 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9858 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9859 mark = gtk_text_buffer_get_insert(buffer);
9860 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9861 offset = gtk_text_iter_get_line_offset(&ins);
9862 if (gtk_text_iter_backward_line(&ins)) {
9863 gtk_text_iter_set_line_offset(&ins, offset);
9864 gtk_text_buffer_place_cursor(buffer, &ins);
9868 static void textview_delete_forward_character (GtkTextView *text)
9870 GtkTextBuffer *buffer;
9872 GtkTextIter ins, end_iter;
9874 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9876 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9877 mark = gtk_text_buffer_get_insert(buffer);
9878 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9880 if (gtk_text_iter_forward_char(&end_iter)) {
9881 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9885 static void textview_delete_backward_character (GtkTextView *text)
9887 GtkTextBuffer *buffer;
9889 GtkTextIter ins, end_iter;
9891 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9893 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9894 mark = gtk_text_buffer_get_insert(buffer);
9895 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9897 if (gtk_text_iter_backward_char(&end_iter)) {
9898 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9902 static void textview_delete_forward_word (GtkTextView *text)
9904 GtkTextBuffer *buffer;
9906 GtkTextIter ins, end_iter;
9908 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9910 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9911 mark = gtk_text_buffer_get_insert(buffer);
9912 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9914 if (gtk_text_iter_forward_word_end(&end_iter)) {
9915 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9919 static void textview_delete_backward_word (GtkTextView *text)
9921 GtkTextBuffer *buffer;
9923 GtkTextIter ins, end_iter;
9925 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9927 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9928 mark = gtk_text_buffer_get_insert(buffer);
9929 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9931 if (gtk_text_iter_backward_word_start(&end_iter)) {
9932 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9936 static void textview_delete_line (GtkTextView *text)
9938 GtkTextBuffer *buffer;
9940 GtkTextIter ins, start_iter, end_iter;
9942 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9944 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9945 mark = gtk_text_buffer_get_insert(buffer);
9946 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9949 gtk_text_iter_set_line_offset(&start_iter, 0);
9952 if (gtk_text_iter_ends_line(&end_iter)){
9953 if (!gtk_text_iter_forward_char(&end_iter))
9954 gtk_text_iter_backward_char(&start_iter);
9957 gtk_text_iter_forward_to_line_end(&end_iter);
9958 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9961 static void textview_delete_to_line_end (GtkTextView *text)
9963 GtkTextBuffer *buffer;
9965 GtkTextIter ins, end_iter;
9967 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9969 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9970 mark = gtk_text_buffer_get_insert(buffer);
9971 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9973 if (gtk_text_iter_ends_line(&end_iter))
9974 gtk_text_iter_forward_char(&end_iter);
9976 gtk_text_iter_forward_to_line_end(&end_iter);
9977 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9980 #define DO_ACTION(name, act) { \
9981 if(!strcmp(name, a_name)) { \
9985 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9987 const gchar *a_name = gtk_action_get_name(action);
9988 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9989 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9990 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9991 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9992 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9993 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9994 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9995 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9996 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9997 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9998 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9999 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10000 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10001 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10005 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10007 Compose *compose = (Compose *)data;
10008 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10009 ComposeCallAdvancedAction action = -1;
10011 action = compose_call_advanced_action_from_path(gaction);
10014 void (*do_action) (GtkTextView *text);
10015 } action_table[] = {
10016 {textview_move_beginning_of_line},
10017 {textview_move_forward_character},
10018 {textview_move_backward_character},
10019 {textview_move_forward_word},
10020 {textview_move_backward_word},
10021 {textview_move_end_of_line},
10022 {textview_move_next_line},
10023 {textview_move_previous_line},
10024 {textview_delete_forward_character},
10025 {textview_delete_backward_character},
10026 {textview_delete_forward_word},
10027 {textview_delete_backward_word},
10028 {textview_delete_line},
10029 {textview_delete_to_line_end}
10032 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10034 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10035 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10036 if (action_table[action].do_action)
10037 action_table[action].do_action(text);
10039 g_warning("Not implemented yet.");
10043 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10047 if (GTK_IS_EDITABLE(widget)) {
10048 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10049 gtk_editable_set_position(GTK_EDITABLE(widget),
10052 if (widget->parent && widget->parent->parent
10053 && widget->parent->parent->parent) {
10054 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10055 gint y = widget->allocation.y;
10056 gint height = widget->allocation.height;
10057 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10058 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10060 if (y < (int)shown->value) {
10061 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10063 if (y + height > (int)shown->value + (int)shown->page_size) {
10064 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10065 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10066 y + height - (int)shown->page_size - 1);
10068 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10069 (int)shown->upper - (int)shown->page_size - 1);
10076 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10077 compose->focused_editable = widget;
10079 #ifdef GENERIC_UMPC
10080 if (GTK_IS_TEXT_VIEW(widget)
10081 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10082 g_object_ref(compose->notebook);
10083 g_object_ref(compose->edit_vbox);
10084 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10085 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10086 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10087 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10088 g_object_unref(compose->notebook);
10089 g_object_unref(compose->edit_vbox);
10090 g_signal_handlers_block_by_func(G_OBJECT(widget),
10091 G_CALLBACK(compose_grab_focus_cb),
10093 gtk_widget_grab_focus(widget);
10094 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10095 G_CALLBACK(compose_grab_focus_cb),
10097 } else if (!GTK_IS_TEXT_VIEW(widget)
10098 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10099 g_object_ref(compose->notebook);
10100 g_object_ref(compose->edit_vbox);
10101 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10102 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10103 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10104 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10105 g_object_unref(compose->notebook);
10106 g_object_unref(compose->edit_vbox);
10107 g_signal_handlers_block_by_func(G_OBJECT(widget),
10108 G_CALLBACK(compose_grab_focus_cb),
10110 gtk_widget_grab_focus(widget);
10111 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10112 G_CALLBACK(compose_grab_focus_cb),
10118 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10120 compose->modified = TRUE;
10121 // compose_beautify_paragraph(compose, NULL, TRUE);
10122 #ifndef GENERIC_UMPC
10123 compose_set_title(compose);
10127 static void compose_wrap_cb(GtkAction *action, gpointer data)
10129 Compose *compose = (Compose *)data;
10130 compose_beautify_paragraph(compose, NULL, TRUE);
10133 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10135 Compose *compose = (Compose *)data;
10136 compose_wrap_all_full(compose, TRUE);
10139 static void compose_find_cb(GtkAction *action, gpointer data)
10141 Compose *compose = (Compose *)data;
10143 message_search_compose(compose);
10146 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10149 Compose *compose = (Compose *)data;
10150 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10151 if (compose->autowrap)
10152 compose_wrap_all_full(compose, TRUE);
10153 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10156 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10159 Compose *compose = (Compose *)data;
10160 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10163 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10165 Compose *compose = (Compose *)data;
10167 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10170 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10172 Compose *compose = (Compose *)data;
10174 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10177 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10179 g_free(compose->privacy_system);
10181 compose->privacy_system = g_strdup(account->default_privacy_system);
10182 compose_update_privacy_system_menu_item(compose, warn);
10185 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10187 Compose *compose = (Compose *)data;
10189 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10190 gtk_widget_show(compose->ruler_hbox);
10191 prefs_common.show_ruler = TRUE;
10193 gtk_widget_hide(compose->ruler_hbox);
10194 gtk_widget_queue_resize(compose->edit_vbox);
10195 prefs_common.show_ruler = FALSE;
10199 static void compose_attach_drag_received_cb (GtkWidget *widget,
10200 GdkDragContext *context,
10203 GtkSelectionData *data,
10206 gpointer user_data)
10208 Compose *compose = (Compose *)user_data;
10211 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10213 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10215 ) && gtk_drag_get_source_widget(context) !=
10216 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10217 list = uri_list_extract_filenames((const gchar *)data->data);
10218 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10219 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10220 compose_attach_append
10221 (compose, (const gchar *)tmp->data,
10222 utf8_filename, NULL);
10223 g_free(utf8_filename);
10225 if (list) compose_changed_cb(NULL, compose);
10226 list_free_strings(list);
10228 } else if (gtk_drag_get_source_widget(context)
10229 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10230 /* comes from our summaryview */
10231 SummaryView * summaryview = NULL;
10232 GSList * list = NULL, *cur = NULL;
10234 if (mainwindow_get_mainwindow())
10235 summaryview = mainwindow_get_mainwindow()->summaryview;
10238 list = summary_get_selected_msg_list(summaryview);
10240 for (cur = list; cur; cur = cur->next) {
10241 MsgInfo *msginfo = (MsgInfo *)cur->data;
10242 gchar *file = NULL;
10244 file = procmsg_get_message_file_full(msginfo,
10247 compose_attach_append(compose, (const gchar *)file,
10248 (const gchar *)file, "message/rfc822");
10252 g_slist_free(list);
10256 static gboolean compose_drag_drop(GtkWidget *widget,
10257 GdkDragContext *drag_context,
10259 guint time, gpointer user_data)
10261 /* not handling this signal makes compose_insert_drag_received_cb
10266 static void compose_insert_drag_received_cb (GtkWidget *widget,
10267 GdkDragContext *drag_context,
10270 GtkSelectionData *data,
10273 gpointer user_data)
10275 Compose *compose = (Compose *)user_data;
10278 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10281 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10283 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10285 AlertValue val = G_ALERTDEFAULT;
10287 list = uri_list_extract_filenames((const gchar *)data->data);
10288 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10289 /* Assume a list of no files, and data has ://, is a remote link */
10290 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10291 gchar *tmpfile = get_tmp_file();
10292 str_write_to_file(tmpdata, tmpfile);
10294 compose_insert_file(compose, tmpfile);
10295 claws_unlink(tmpfile);
10297 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10298 compose_beautify_paragraph(compose, NULL, TRUE);
10301 switch (prefs_common.compose_dnd_mode) {
10302 case COMPOSE_DND_ASK:
10303 val = alertpanel_full(_("Insert or attach?"),
10304 _("Do you want to insert the contents of the file(s) "
10305 "into the message body, or attach it to the email?"),
10306 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10307 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10309 case COMPOSE_DND_INSERT:
10310 val = G_ALERTALTERNATE;
10312 case COMPOSE_DND_ATTACH:
10313 val = G_ALERTOTHER;
10316 /* unexpected case */
10317 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10320 if (val & G_ALERTDISABLE) {
10321 val &= ~G_ALERTDISABLE;
10322 /* remember what action to perform by default, only if we don't click Cancel */
10323 if (val == G_ALERTALTERNATE)
10324 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10325 else if (val == G_ALERTOTHER)
10326 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10329 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10330 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10331 list_free_strings(list);
10334 } else if (val == G_ALERTOTHER) {
10335 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10336 list_free_strings(list);
10341 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10342 compose_insert_file(compose, (const gchar *)tmp->data);
10344 list_free_strings(list);
10346 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10349 #if GTK_CHECK_VERSION(2, 8, 0)
10350 /* do nothing, handled by GTK */
10352 gchar *tmpfile = get_tmp_file();
10353 str_write_to_file((const gchar *)data->data, tmpfile);
10354 compose_insert_file(compose, tmpfile);
10355 claws_unlink(tmpfile);
10357 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10361 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10364 static void compose_header_drag_received_cb (GtkWidget *widget,
10365 GdkDragContext *drag_context,
10368 GtkSelectionData *data,
10371 gpointer user_data)
10373 GtkEditable *entry = (GtkEditable *)user_data;
10374 gchar *email = (gchar *)data->data;
10376 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10379 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10380 gchar *decoded=g_new(gchar, strlen(email));
10383 email += strlen("mailto:");
10384 decode_uri(decoded, email); /* will fit */
10385 gtk_editable_delete_text(entry, 0, -1);
10386 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10387 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10391 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10394 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10396 Compose *compose = (Compose *)data;
10398 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10399 compose->return_receipt = TRUE;
10401 compose->return_receipt = FALSE;
10404 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10406 Compose *compose = (Compose *)data;
10408 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10409 compose->remove_references = TRUE;
10411 compose->remove_references = FALSE;
10414 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10415 ComposeHeaderEntry *headerentry)
10417 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10421 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10422 GdkEventKey *event,
10423 ComposeHeaderEntry *headerentry)
10425 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10426 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10427 !(event->state & GDK_MODIFIER_MASK) &&
10428 (event->keyval == GDK_BackSpace) &&
10429 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10430 gtk_container_remove
10431 (GTK_CONTAINER(headerentry->compose->header_table),
10432 headerentry->combo);
10433 gtk_container_remove
10434 (GTK_CONTAINER(headerentry->compose->header_table),
10435 headerentry->entry);
10436 headerentry->compose->header_list =
10437 g_slist_remove(headerentry->compose->header_list,
10439 g_free(headerentry);
10440 } else if (event->keyval == GDK_Tab) {
10441 if (headerentry->compose->header_last == headerentry) {
10442 /* Override default next focus, and give it to subject_entry
10443 * instead of notebook tabs
10445 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10446 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10453 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10454 ComposeHeaderEntry *headerentry)
10456 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10457 compose_create_header_entry(headerentry->compose);
10458 g_signal_handlers_disconnect_matched
10459 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10460 0, 0, NULL, NULL, headerentry);
10462 /* Automatically scroll down */
10463 compose_show_first_last_header(headerentry->compose, FALSE);
10469 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10471 GtkAdjustment *vadj;
10473 cm_return_if_fail(compose);
10474 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10475 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10477 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10478 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10479 gtk_adjustment_changed(vadj);
10482 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10483 const gchar *text, gint len, Compose *compose)
10485 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10486 (G_OBJECT(compose->text), "paste_as_quotation"));
10489 cm_return_if_fail(text != NULL);
10491 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10492 G_CALLBACK(text_inserted),
10494 if (paste_as_quotation) {
10496 const gchar *qmark;
10498 GtkTextIter start_iter;
10501 len = strlen(text);
10503 new_text = g_strndup(text, len);
10505 qmark = compose_quote_char_from_context(compose);
10507 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10508 gtk_text_buffer_place_cursor(buffer, iter);
10510 pos = gtk_text_iter_get_offset(iter);
10512 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10513 _("Quote format error at line %d."));
10514 quote_fmt_reset_vartable();
10516 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10517 GINT_TO_POINTER(paste_as_quotation - 1));
10519 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10520 gtk_text_buffer_place_cursor(buffer, iter);
10521 gtk_text_buffer_delete_mark(buffer, mark);
10523 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10524 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10525 compose_beautify_paragraph(compose, &start_iter, FALSE);
10526 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10527 gtk_text_buffer_delete_mark(buffer, mark);
10529 if (strcmp(text, "\n") || compose->automatic_break
10530 || gtk_text_iter_starts_line(iter)) {
10531 GtkTextIter before_ins;
10532 gtk_text_buffer_insert(buffer, iter, text, len);
10533 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10534 before_ins = *iter;
10535 gtk_text_iter_backward_chars(&before_ins, len);
10536 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10539 /* check if the preceding is just whitespace or quote */
10540 GtkTextIter start_line;
10541 gchar *tmp = NULL, *quote = NULL;
10542 gint quote_len = 0, is_normal = 0;
10543 start_line = *iter;
10544 gtk_text_iter_set_line_offset(&start_line, 0);
10545 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10548 if (*tmp == '\0') {
10551 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10559 gtk_text_buffer_insert(buffer, iter, text, len);
10561 gtk_text_buffer_insert_with_tags_by_name(buffer,
10562 iter, text, len, "no_join", NULL);
10567 if (!paste_as_quotation) {
10568 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10569 compose_beautify_paragraph(compose, iter, FALSE);
10570 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10571 gtk_text_buffer_delete_mark(buffer, mark);
10574 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10575 G_CALLBACK(text_inserted),
10577 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10579 if (prefs_common.autosave &&
10580 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10581 compose->draft_timeout_tag != -2 /* disabled while loading */)
10582 compose->draft_timeout_tag = g_timeout_add
10583 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10585 static gint compose_defer_auto_save_draft(Compose *compose)
10587 compose->draft_timeout_tag = -1;
10588 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10593 static void compose_check_all(GtkAction *action, gpointer data)
10595 Compose *compose = (Compose *)data;
10596 if (!compose->gtkaspell)
10599 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10600 claws_spell_entry_check_all(
10601 CLAWS_SPELL_ENTRY(compose->subject_entry));
10603 gtkaspell_check_all(compose->gtkaspell);
10606 static void compose_highlight_all(GtkAction *action, gpointer data)
10608 Compose *compose = (Compose *)data;
10609 if (compose->gtkaspell) {
10610 claws_spell_entry_recheck_all(
10611 CLAWS_SPELL_ENTRY(compose->subject_entry));
10612 gtkaspell_highlight_all(compose->gtkaspell);
10616 static void compose_check_backwards(GtkAction *action, gpointer data)
10618 Compose *compose = (Compose *)data;
10619 if (!compose->gtkaspell) {
10620 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10624 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10625 claws_spell_entry_check_backwards(
10626 CLAWS_SPELL_ENTRY(compose->subject_entry));
10628 gtkaspell_check_backwards(compose->gtkaspell);
10631 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10633 Compose *compose = (Compose *)data;
10634 if (!compose->gtkaspell) {
10635 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10639 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10640 claws_spell_entry_check_forwards_go(
10641 CLAWS_SPELL_ENTRY(compose->subject_entry));
10643 gtkaspell_check_forwards_go(compose->gtkaspell);
10648 *\brief Guess originating forward account from MsgInfo and several
10649 * "common preference" settings. Return NULL if no guess.
10651 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10653 PrefsAccount *account = NULL;
10655 cm_return_val_if_fail(msginfo, NULL);
10656 cm_return_val_if_fail(msginfo->folder, NULL);
10657 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10659 if (msginfo->folder->prefs->enable_default_account)
10660 account = account_find_from_id(msginfo->folder->prefs->default_account);
10663 account = msginfo->folder->folder->account;
10665 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10667 Xstrdup_a(to, msginfo->to, return NULL);
10668 extract_address(to);
10669 account = account_find_from_address(to, FALSE);
10672 if (!account && prefs_common.forward_account_autosel) {
10673 gchar cc[BUFFSIZE];
10674 if (!procheader_get_header_from_msginfo
10675 (msginfo, cc,sizeof cc , "Cc:")) {
10676 gchar *buf = cc + strlen("Cc:");
10677 extract_address(buf);
10678 account = account_find_from_address(buf, FALSE);
10682 if (!account && prefs_common.forward_account_autosel) {
10683 gchar deliveredto[BUFFSIZE];
10684 if (!procheader_get_header_from_msginfo
10685 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10686 gchar *buf = deliveredto + strlen("Delivered-To:");
10687 extract_address(buf);
10688 account = account_find_from_address(buf, FALSE);
10695 gboolean compose_close(Compose *compose)
10699 if (!g_mutex_trylock(compose->mutex)) {
10700 /* we have to wait for the (possibly deferred by auto-save)
10701 * drafting to be done, before destroying the compose under
10703 debug_print("waiting for drafting to finish...\n");
10704 compose_allow_user_actions(compose, FALSE);
10705 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10708 cm_return_val_if_fail(compose, FALSE);
10709 gtkut_widget_get_uposition(compose->window, &x, &y);
10710 if (!compose->batch) {
10711 prefs_common.compose_x = x;
10712 prefs_common.compose_y = y;
10714 g_mutex_unlock(compose->mutex);
10715 compose_destroy(compose);
10720 * Add entry field for each address in list.
10721 * \param compose E-Mail composition object.
10722 * \param listAddress List of (formatted) E-Mail addresses.
10724 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10727 node = listAddress;
10729 addr = ( gchar * ) node->data;
10730 compose_entry_append( compose, addr, COMPOSE_TO );
10731 node = g_list_next( node );
10735 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10736 guint action, gboolean opening_multiple)
10738 gchar *body = NULL;
10739 GSList *new_msglist = NULL;
10740 MsgInfo *tmp_msginfo = NULL;
10741 gboolean originally_enc = FALSE;
10742 gboolean originally_sig = FALSE;
10743 Compose *compose = NULL;
10744 gchar *s_system = NULL;
10746 cm_return_if_fail(msgview != NULL);
10748 cm_return_if_fail(msginfo_list != NULL);
10750 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10751 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10752 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10754 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10755 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10756 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10757 orig_msginfo, mimeinfo);
10758 if (tmp_msginfo != NULL) {
10759 new_msglist = g_slist_append(NULL, tmp_msginfo);
10761 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10762 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10763 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10765 tmp_msginfo->folder = orig_msginfo->folder;
10766 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10767 if (orig_msginfo->tags) {
10768 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10769 tmp_msginfo->folder->tags_dirty = TRUE;
10775 if (!opening_multiple)
10776 body = messageview_get_selection(msgview);
10779 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10780 procmsg_msginfo_free(tmp_msginfo);
10781 g_slist_free(new_msglist);
10783 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10785 if (compose && originally_enc) {
10786 compose_force_encryption(compose, compose->account, FALSE, s_system);
10789 if (compose && originally_sig && compose->account->default_sign_reply) {
10790 compose_force_signing(compose, compose->account, s_system);
10796 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10799 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10800 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10801 GSList *cur = msginfo_list;
10802 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10803 "messages. Opening the windows "
10804 "could take some time. Do you "
10805 "want to continue?"),
10806 g_slist_length(msginfo_list));
10807 if (g_slist_length(msginfo_list) > 9
10808 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10809 != G_ALERTALTERNATE) {
10814 /* We'll open multiple compose windows */
10815 /* let the WM place the next windows */
10816 compose_force_window_origin = FALSE;
10817 for (; cur; cur = cur->next) {
10819 tmplist.data = cur->data;
10820 tmplist.next = NULL;
10821 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10823 compose_force_window_origin = TRUE;
10825 /* forwarding multiple mails as attachments is done via a
10826 * single compose window */
10827 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10831 void compose_set_position(Compose *compose, gint pos)
10833 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10835 gtkut_text_view_set_position(text, pos);
10838 gboolean compose_search_string(Compose *compose,
10839 const gchar *str, gboolean case_sens)
10841 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10843 return gtkut_text_view_search_string(text, str, case_sens);
10846 gboolean compose_search_string_backward(Compose *compose,
10847 const gchar *str, gboolean case_sens)
10849 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10851 return gtkut_text_view_search_string_backward(text, str, case_sens);
10854 /* allocate a msginfo structure and populate its data from a compose data structure */
10855 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10857 MsgInfo *newmsginfo;
10859 gchar buf[BUFFSIZE];
10861 cm_return_val_if_fail( compose != NULL, NULL );
10863 newmsginfo = procmsg_msginfo_new();
10866 get_rfc822_date(buf, sizeof(buf));
10867 newmsginfo->date = g_strdup(buf);
10870 if (compose->from_name) {
10871 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10872 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10876 if (compose->subject_entry)
10877 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10879 /* to, cc, reply-to, newsgroups */
10880 for (list = compose->header_list; list; list = list->next) {
10881 gchar *header = gtk_editable_get_chars(
10883 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10884 gchar *entry = gtk_editable_get_chars(
10885 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10887 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10888 if ( newmsginfo->to == NULL ) {
10889 newmsginfo->to = g_strdup(entry);
10890 } else if (entry && *entry) {
10891 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10892 g_free(newmsginfo->to);
10893 newmsginfo->to = tmp;
10896 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10897 if ( newmsginfo->cc == NULL ) {
10898 newmsginfo->cc = g_strdup(entry);
10899 } else if (entry && *entry) {
10900 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10901 g_free(newmsginfo->cc);
10902 newmsginfo->cc = tmp;
10905 if ( strcasecmp(header,
10906 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10907 if ( newmsginfo->newsgroups == NULL ) {
10908 newmsginfo->newsgroups = g_strdup(entry);
10909 } else if (entry && *entry) {
10910 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10911 g_free(newmsginfo->newsgroups);
10912 newmsginfo->newsgroups = tmp;
10920 /* other data is unset */
10926 /* update compose's dictionaries from folder dict settings */
10927 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10928 FolderItem *folder_item)
10930 cm_return_if_fail(compose != NULL);
10932 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10933 FolderItemPrefs *prefs = folder_item->prefs;
10935 if (prefs->enable_default_dictionary)
10936 gtkaspell_change_dict(compose->gtkaspell,
10937 prefs->default_dictionary, FALSE);
10938 if (folder_item->prefs->enable_default_alt_dictionary)
10939 gtkaspell_change_alt_dict(compose->gtkaspell,
10940 prefs->default_alt_dictionary);
10941 if (prefs->enable_default_dictionary
10942 || prefs->enable_default_alt_dictionary)
10943 compose_spell_menu_changed(compose);