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_destroy (Compose *compose);
210 static void compose_entries_set (Compose *compose,
212 ComposeEntryType to_type);
213 static gint compose_parse_header (Compose *compose,
215 static gchar *compose_parse_references (const gchar *ref,
218 static gchar *compose_quote_fmt (Compose *compose,
224 gboolean need_unescape,
225 const gchar *err_msg);
227 static void compose_reply_set_entry (Compose *compose,
233 followup_and_reply_to);
234 static void compose_reedit_set_entry (Compose *compose,
237 static void compose_insert_sig (Compose *compose,
239 static ComposeInsertResult compose_insert_file (Compose *compose,
242 static gboolean compose_attach_append (Compose *compose,
245 const gchar *content_type);
246 static void compose_attach_parts (Compose *compose,
249 static gboolean compose_beautify_paragraph (Compose *compose,
250 GtkTextIter *par_iter,
252 static void compose_wrap_all (Compose *compose);
253 static void compose_wrap_all_full (Compose *compose,
256 static void compose_set_title (Compose *compose);
257 static void compose_select_account (Compose *compose,
258 PrefsAccount *account,
261 static PrefsAccount *compose_current_mail_account(void);
262 /* static gint compose_send (Compose *compose); */
263 static gboolean compose_check_for_valid_recipient
265 static gboolean compose_check_entries (Compose *compose,
266 gboolean check_everything);
267 static gint compose_write_to_file (Compose *compose,
270 gboolean attach_parts);
271 static gint compose_write_body_to_file (Compose *compose,
273 static gint compose_remove_reedit_target (Compose *compose,
275 static void compose_remove_draft (Compose *compose);
276 static gint compose_queue_sub (Compose *compose,
280 gboolean check_subject,
281 gboolean remove_reedit_target);
282 static int compose_add_attachments (Compose *compose,
284 static gchar *compose_get_header (Compose *compose);
286 static void compose_convert_header (Compose *compose,
291 gboolean addr_field);
293 static void compose_attach_info_free (AttachInfo *ainfo);
294 static void compose_attach_remove_selected (GtkAction *action,
297 static void compose_template_apply (Compose *compose,
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 _("The body of the \"New message\" template has an 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(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
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 _("The body of the \"Reply\" template has an 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(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
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 _("The body of the \"Forward\" template has an 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(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
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 _("The body of the \"Redirect\" template has an 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(_("The \"Quotation mark\" of the template is invalid."));
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) {
5069 if (compose->account->msgid_with_addr) {
5070 addr = compose->account->address;
5072 generate_msgid(buf, sizeof(buf), addr);
5073 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5074 compose->msgid = g_strdup(buf);
5076 compose->msgid = NULL;
5079 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5082 /* separator between header and body */
5083 err |= (fputs("\n", fp) == EOF);
5085 return (err ? -1:0);
5088 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5092 gchar buf[BUFFSIZE];
5094 gboolean skip = FALSE;
5095 gboolean err = FALSE;
5096 gchar *not_included[]={
5097 "Return-Path:", "Delivered-To:", "Received:",
5098 "Subject:", "X-UIDL:", "AF:",
5099 "NF:", "PS:", "SRH:",
5100 "SFN:", "DSR:", "MID:",
5101 "CFG:", "PT:", "S:",
5102 "RQ:", "SSV:", "NSV:",
5103 "SSH:", "R:", "MAID:",
5104 "NAID:", "RMID:", "FMID:",
5105 "SCF:", "RRCPT:", "NG:",
5106 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5107 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5108 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5109 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5110 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5113 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5114 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5118 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5120 for (i = 0; not_included[i] != NULL; i++) {
5121 if (g_ascii_strncasecmp(buf, not_included[i],
5122 strlen(not_included[i])) == 0) {
5129 if (fputs(buf, fdest) == -1)
5132 if (!prefs_common.redirect_keep_from) {
5133 if (g_ascii_strncasecmp(buf, "From:",
5134 strlen("From:")) == 0) {
5135 err |= (fputs(" (by way of ", fdest) == EOF);
5136 if (compose->account->name
5137 && *compose->account->name) {
5138 compose_convert_header
5139 (compose, buf, sizeof(buf),
5140 compose->account->name,
5143 err |= (fprintf(fdest, "%s <%s>",
5145 compose->account->address) < 0);
5147 err |= (fprintf(fdest, "%s",
5148 compose->account->address) < 0);
5149 err |= (fputs(")", fdest) == EOF);
5153 if (fputs("\n", fdest) == -1)
5160 if (compose_redirect_write_headers(compose, fdest))
5163 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5164 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5177 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5179 GtkTextBuffer *buffer;
5180 GtkTextIter start, end;
5183 const gchar *out_codeset;
5184 EncodingType encoding = ENC_UNKNOWN;
5185 MimeInfo *mimemsg, *mimetext;
5187 const gchar *src_codeset = CS_INTERNAL;
5188 gchar *from_addr = NULL;
5189 gchar *from_name = NULL;
5191 if (action == COMPOSE_WRITE_FOR_SEND)
5192 attach_parts = TRUE;
5194 /* create message MimeInfo */
5195 mimemsg = procmime_mimeinfo_new();
5196 mimemsg->type = MIMETYPE_MESSAGE;
5197 mimemsg->subtype = g_strdup("rfc822");
5198 mimemsg->content = MIMECONTENT_MEM;
5199 mimemsg->tmp = TRUE; /* must free content later */
5200 mimemsg->data.mem = compose_get_header(compose);
5202 /* Create text part MimeInfo */
5203 /* get all composed text */
5204 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5205 gtk_text_buffer_get_start_iter(buffer, &start);
5206 gtk_text_buffer_get_end_iter(buffer, &end);
5207 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5209 out_codeset = conv_get_charset_str(compose->out_encoding);
5211 if (!out_codeset && is_ascii_str(chars)) {
5212 out_codeset = CS_US_ASCII;
5213 } else if (prefs_common.outgoing_fallback_to_ascii &&
5214 is_ascii_str(chars)) {
5215 out_codeset = CS_US_ASCII;
5216 encoding = ENC_7BIT;
5220 gchar *test_conv_global_out = NULL;
5221 gchar *test_conv_reply = NULL;
5223 /* automatic mode. be automatic. */
5224 codeconv_set_strict(TRUE);
5226 out_codeset = conv_get_outgoing_charset_str();
5228 debug_print("trying to convert to %s\n", out_codeset);
5229 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5232 if (!test_conv_global_out && compose->orig_charset
5233 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5234 out_codeset = compose->orig_charset;
5235 debug_print("failure; trying to convert to %s\n", out_codeset);
5236 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5239 if (!test_conv_global_out && !test_conv_reply) {
5241 out_codeset = CS_INTERNAL;
5242 debug_print("failure; finally using %s\n", out_codeset);
5244 g_free(test_conv_global_out);
5245 g_free(test_conv_reply);
5246 codeconv_set_strict(FALSE);
5249 if (encoding == ENC_UNKNOWN) {
5250 if (prefs_common.encoding_method == CTE_BASE64)
5251 encoding = ENC_BASE64;
5252 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5253 encoding = ENC_QUOTED_PRINTABLE;
5254 else if (prefs_common.encoding_method == CTE_8BIT)
5255 encoding = ENC_8BIT;
5257 encoding = procmime_get_encoding_for_charset(out_codeset);
5260 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5261 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5263 if (action == COMPOSE_WRITE_FOR_SEND) {
5264 codeconv_set_strict(TRUE);
5265 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5266 codeconv_set_strict(FALSE);
5272 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5273 "to the specified %s charset.\n"
5274 "Send it as %s?"), out_codeset, src_codeset);
5275 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5276 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5279 if (aval != G_ALERTALTERNATE) {
5284 out_codeset = src_codeset;
5290 out_codeset = src_codeset;
5295 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5296 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5297 strstr(buf, "\nFrom ") != NULL) {
5298 encoding = ENC_QUOTED_PRINTABLE;
5302 mimetext = procmime_mimeinfo_new();
5303 mimetext->content = MIMECONTENT_MEM;
5304 mimetext->tmp = TRUE; /* must free content later */
5305 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5306 * and free the data, which we need later. */
5307 mimetext->data.mem = g_strdup(buf);
5308 mimetext->type = MIMETYPE_TEXT;
5309 mimetext->subtype = g_strdup("plain");
5310 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5311 g_strdup(out_codeset));
5313 /* protect trailing spaces when signing message */
5314 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5315 privacy_system_can_sign(compose->privacy_system)) {
5316 encoding = ENC_QUOTED_PRINTABLE;
5319 debug_print("main text: %zd bytes encoded as %s in %d\n",
5320 strlen(buf), out_codeset, encoding);
5322 /* check for line length limit */
5323 if (action == COMPOSE_WRITE_FOR_SEND &&
5324 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5325 check_line_length(buf, 1000, &line) < 0) {
5329 msg = g_strdup_printf
5330 (_("Line %d exceeds the line length limit (998 bytes).\n"
5331 "The contents of the message might be broken on the way to the delivery.\n"
5333 "Send it anyway?"), line + 1);
5334 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5336 if (aval != G_ALERTALTERNATE) {
5342 if (encoding != ENC_UNKNOWN)
5343 procmime_encode_content(mimetext, encoding);
5345 /* append attachment parts */
5346 if (compose_use_attach(compose) && attach_parts) {
5347 MimeInfo *mimempart;
5348 gchar *boundary = NULL;
5349 mimempart = procmime_mimeinfo_new();
5350 mimempart->content = MIMECONTENT_EMPTY;
5351 mimempart->type = MIMETYPE_MULTIPART;
5352 mimempart->subtype = g_strdup("mixed");
5356 boundary = generate_mime_boundary(NULL);
5357 } while (strstr(buf, boundary) != NULL);
5359 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5362 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5364 g_node_append(mimempart->node, mimetext->node);
5365 g_node_append(mimemsg->node, mimempart->node);
5367 if (compose_add_attachments(compose, mimempart) < 0)
5370 g_node_append(mimemsg->node, mimetext->node);
5374 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5375 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5376 /* extract name and address */
5377 if (strstr(spec, " <") && strstr(spec, ">")) {
5378 from_addr = g_strdup(strrchr(spec, '<')+1);
5379 *(strrchr(from_addr, '>')) = '\0';
5380 from_name = g_strdup(spec);
5381 *(strrchr(from_name, '<')) = '\0';
5388 /* sign message if sending */
5389 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5390 privacy_system_can_sign(compose->privacy_system))
5391 if (!privacy_sign(compose->privacy_system, mimemsg,
5392 compose->account, from_addr)) {
5399 procmime_write_mimeinfo(mimemsg, fp);
5401 procmime_mimeinfo_free_all(mimemsg);
5406 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5408 GtkTextBuffer *buffer;
5409 GtkTextIter start, end;
5414 if ((fp = g_fopen(file, "wb")) == NULL) {
5415 FILE_OP_ERROR(file, "fopen");
5419 /* chmod for security */
5420 if (change_file_mode_rw(fp, file) < 0) {
5421 FILE_OP_ERROR(file, "chmod");
5422 g_warning("can't change file mode\n");
5425 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5426 gtk_text_buffer_get_start_iter(buffer, &start);
5427 gtk_text_buffer_get_end_iter(buffer, &end);
5428 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5430 chars = conv_codeset_strdup
5431 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5434 if (!chars) return -1;
5437 len = strlen(chars);
5438 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5439 FILE_OP_ERROR(file, "fwrite");
5448 if (fclose(fp) == EOF) {
5449 FILE_OP_ERROR(file, "fclose");
5456 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5459 MsgInfo *msginfo = compose->targetinfo;
5461 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5462 if (!msginfo) return -1;
5464 if (!force && MSG_IS_LOCKED(msginfo->flags))
5467 item = msginfo->folder;
5468 cm_return_val_if_fail(item != NULL, -1);
5470 if (procmsg_msg_exist(msginfo) &&
5471 (folder_has_parent_of_type(item, F_QUEUE) ||
5472 folder_has_parent_of_type(item, F_DRAFT)
5473 || msginfo == compose->autosaved_draft)) {
5474 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5475 g_warning("can't remove the old message\n");
5478 debug_print("removed reedit target %d\n", msginfo->msgnum);
5485 static void compose_remove_draft(Compose *compose)
5488 MsgInfo *msginfo = compose->targetinfo;
5489 drafts = account_get_special_folder(compose->account, F_DRAFT);
5491 if (procmsg_msg_exist(msginfo)) {
5492 folder_item_remove_msg(drafts, msginfo->msgnum);
5497 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5498 gboolean remove_reedit_target)
5500 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5503 static gboolean compose_warn_encryption(Compose *compose)
5505 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5506 AlertValue val = G_ALERTALTERNATE;
5508 if (warning == NULL)
5511 val = alertpanel_full(_("Encryption warning"), warning,
5512 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5513 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5514 if (val & G_ALERTDISABLE) {
5515 val &= ~G_ALERTDISABLE;
5516 if (val == G_ALERTALTERNATE)
5517 privacy_inhibit_encrypt_warning(compose->privacy_system,
5521 if (val == G_ALERTALTERNATE) {
5528 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5529 gchar **msgpath, gboolean check_subject,
5530 gboolean remove_reedit_target)
5537 static gboolean lock = FALSE;
5538 PrefsAccount *mailac = NULL, *newsac = NULL;
5539 gboolean err = FALSE;
5541 debug_print("queueing message...\n");
5542 cm_return_val_if_fail(compose->account != NULL, -1);
5546 if (compose_check_entries(compose, check_subject) == FALSE) {
5548 if (compose->batch) {
5549 gtk_widget_show_all(compose->window);
5554 if (!compose->to_list && !compose->newsgroup_list) {
5555 g_warning("can't get recipient list.");
5560 if (compose->to_list) {
5561 if (compose->account->protocol != A_NNTP)
5562 mailac = compose->account;
5563 else if (cur_account && cur_account->protocol != A_NNTP)
5564 mailac = cur_account;
5565 else if (!(mailac = compose_current_mail_account())) {
5567 alertpanel_error(_("No account for sending mails available!"));
5572 if (compose->newsgroup_list) {
5573 if (compose->account->protocol == A_NNTP)
5574 newsac = compose->account;
5577 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5582 /* write queue header */
5583 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5584 G_DIR_SEPARATOR, compose, (guint) rand());
5585 debug_print("queuing to %s\n", tmp);
5586 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5587 FILE_OP_ERROR(tmp, "fopen");
5593 if (change_file_mode_rw(fp, tmp) < 0) {
5594 FILE_OP_ERROR(tmp, "chmod");
5595 g_warning("can't change file mode\n");
5598 /* queueing variables */
5599 err |= (fprintf(fp, "AF:\n") < 0);
5600 err |= (fprintf(fp, "NF:0\n") < 0);
5601 err |= (fprintf(fp, "PS:10\n") < 0);
5602 err |= (fprintf(fp, "SRH:1\n") < 0);
5603 err |= (fprintf(fp, "SFN:\n") < 0);
5604 err |= (fprintf(fp, "DSR:\n") < 0);
5606 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5608 err |= (fprintf(fp, "MID:\n") < 0);
5609 err |= (fprintf(fp, "CFG:\n") < 0);
5610 err |= (fprintf(fp, "PT:0\n") < 0);
5611 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5612 err |= (fprintf(fp, "RQ:\n") < 0);
5614 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5616 err |= (fprintf(fp, "SSV:\n") < 0);
5618 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5620 err |= (fprintf(fp, "NSV:\n") < 0);
5621 err |= (fprintf(fp, "SSH:\n") < 0);
5622 /* write recepient list */
5623 if (compose->to_list) {
5624 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5625 for (cur = compose->to_list->next; cur != NULL;
5627 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5628 err |= (fprintf(fp, "\n") < 0);
5630 /* write newsgroup list */
5631 if (compose->newsgroup_list) {
5632 err |= (fprintf(fp, "NG:") < 0);
5633 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5634 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5635 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5636 err |= (fprintf(fp, "\n") < 0);
5638 /* Sylpheed account IDs */
5640 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5642 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5645 if (compose->privacy_system != NULL) {
5646 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5647 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5648 if (compose->use_encryption) {
5650 if (!compose_warn_encryption(compose)) {
5657 if (mailac && mailac->encrypt_to_self) {
5658 GSList *tmp_list = g_slist_copy(compose->to_list);
5659 tmp_list = g_slist_append(tmp_list, compose->account->address);
5660 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5661 g_slist_free(tmp_list);
5663 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5665 if (encdata != NULL) {
5666 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5667 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5668 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5670 } /* else we finally dont want to encrypt */
5672 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5673 /* and if encdata was null, it means there's been a problem in
5685 /* Save copy folder */
5686 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5687 gchar *savefolderid;
5689 savefolderid = compose_get_save_to(compose);
5690 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5691 g_free(savefolderid);
5693 /* Save copy folder */
5694 if (compose->return_receipt) {
5695 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5697 /* Message-ID of message replying to */
5698 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5701 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5702 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5705 /* Message-ID of message forwarding to */
5706 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5709 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5710 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5714 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5715 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5717 /* end of headers */
5718 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5720 if (compose->redirect_filename != NULL) {
5721 if (compose_redirect_write_to_file(compose, fp) < 0) {
5730 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5735 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5739 g_warning("failed to write queue message\n");
5746 if (fclose(fp) == EOF) {
5747 FILE_OP_ERROR(tmp, "fclose");
5754 if (item && *item) {
5757 queue = account_get_special_folder(compose->account, F_QUEUE);
5760 g_warning("can't find queue folder\n");
5766 folder_item_scan(queue);
5767 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5768 g_warning("can't queue the message\n");
5775 if (msgpath == NULL) {
5781 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5782 compose_remove_reedit_target(compose, FALSE);
5785 if ((msgnum != NULL) && (item != NULL)) {
5793 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5796 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5798 struct stat statbuf;
5799 gchar *type, *subtype;
5800 GtkTreeModel *model;
5803 model = gtk_tree_view_get_model(tree_view);
5805 if (!gtk_tree_model_get_iter_first(model, &iter))
5808 gtk_tree_model_get(model, &iter,
5812 if (!is_file_exist(ainfo->file)) {
5813 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5814 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5815 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5817 if (val == G_ALERTDEFAULT) {
5822 mimepart = procmime_mimeinfo_new();
5823 mimepart->content = MIMECONTENT_FILE;
5824 mimepart->data.filename = g_strdup(ainfo->file);
5825 mimepart->tmp = FALSE; /* or we destroy our attachment */
5826 mimepart->offset = 0;
5828 g_stat(ainfo->file, &statbuf);
5829 mimepart->length = statbuf.st_size;
5831 type = g_strdup(ainfo->content_type);
5833 if (!strchr(type, '/')) {
5835 type = g_strdup("application/octet-stream");
5838 subtype = strchr(type, '/') + 1;
5839 *(subtype - 1) = '\0';
5840 mimepart->type = procmime_get_media_type(type);
5841 mimepart->subtype = g_strdup(subtype);
5844 if (mimepart->type == MIMETYPE_MESSAGE &&
5845 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5846 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5849 if (mimepart->type == MIMETYPE_APPLICATION &&
5850 !strcmp2(mimepart->subtype, "octet-stream"))
5851 g_hash_table_insert(mimepart->typeparameters,
5852 g_strdup("name"), g_strdup(ainfo->name));
5853 g_hash_table_insert(mimepart->dispositionparameters,
5854 g_strdup("filename"), g_strdup(ainfo->name));
5855 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5859 if (compose->use_signing) {
5860 if (ainfo->encoding == ENC_7BIT)
5861 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5862 else if (ainfo->encoding == ENC_8BIT)
5863 ainfo->encoding = ENC_BASE64;
5866 procmime_encode_content(mimepart, ainfo->encoding);
5868 g_node_append(parent->node, mimepart->node);
5869 } while (gtk_tree_model_iter_next(model, &iter));
5874 #define IS_IN_CUSTOM_HEADER(header) \
5875 (compose->account->add_customhdr && \
5876 custom_header_find(compose->account->customhdr_list, header) != NULL)
5878 static void compose_add_headerfield_from_headerlist(Compose *compose,
5880 const gchar *fieldname,
5881 const gchar *seperator)
5883 gchar *str, *fieldname_w_colon;
5884 gboolean add_field = FALSE;
5886 ComposeHeaderEntry *headerentry;
5887 const gchar *headerentryname;
5888 const gchar *trans_fieldname;
5891 if (IS_IN_CUSTOM_HEADER(fieldname))
5894 debug_print("Adding %s-fields\n", fieldname);
5896 fieldstr = g_string_sized_new(64);
5898 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5899 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5901 for (list = compose->header_list; list; list = list->next) {
5902 headerentry = ((ComposeHeaderEntry *)list->data);
5903 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5905 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5906 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5908 if (str[0] != '\0') {
5910 g_string_append(fieldstr, seperator);
5911 g_string_append(fieldstr, str);
5920 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5921 compose_convert_header
5922 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5923 strlen(fieldname) + 2, TRUE);
5924 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5928 g_free(fieldname_w_colon);
5929 g_string_free(fieldstr, TRUE);
5934 static gchar *compose_get_header(Compose *compose)
5936 gchar buf[BUFFSIZE];
5937 const gchar *entry_str;
5941 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5943 gchar *from_name = NULL, *from_address = NULL;
5946 cm_return_val_if_fail(compose->account != NULL, NULL);
5947 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5949 header = g_string_sized_new(64);
5952 get_rfc822_date(buf, sizeof(buf));
5953 g_string_append_printf(header, "Date: %s\n", buf);
5957 if (compose->account->name && *compose->account->name) {
5959 QUOTE_IF_REQUIRED(buf, compose->account->name);
5960 tmp = g_strdup_printf("%s <%s>",
5961 buf, compose->account->address);
5963 tmp = g_strdup_printf("%s",
5964 compose->account->address);
5966 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5967 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5969 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5970 from_address = g_strdup(compose->account->address);
5972 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5973 /* extract name and address */
5974 if (strstr(spec, " <") && strstr(spec, ">")) {
5975 from_address = g_strdup(strrchr(spec, '<')+1);
5976 *(strrchr(from_address, '>')) = '\0';
5977 from_name = g_strdup(spec);
5978 *(strrchr(from_name, '<')) = '\0';
5981 from_address = g_strdup(spec);
5988 if (from_name && *from_name) {
5989 compose_convert_header
5990 (compose, buf, sizeof(buf), from_name,
5991 strlen("From: "), TRUE);
5992 QUOTE_IF_REQUIRED(name, buf);
5994 g_string_append_printf(header, "From: %s <%s>\n",
5995 name, from_address);
5997 g_string_append_printf(header, "From: %s\n", from_address);
6000 g_free(from_address);
6003 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6006 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6009 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6012 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6015 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6017 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6020 compose_convert_header(compose, buf, sizeof(buf), str,
6021 strlen("Subject: "), FALSE);
6022 g_string_append_printf(header, "Subject: %s\n", buf);
6028 if (compose->account->set_domain && compose->account->domain) {
6029 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6030 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6031 g_snprintf(buf, sizeof(buf), "%s",
6032 strchr(compose->account->address, '@') ?
6033 strchr(compose->account->address, '@')+1 :
6034 compose->account->address);
6036 g_snprintf(buf, sizeof(buf), "%s", "");
6039 if (compose->account->gen_msgid) {
6041 if (compose->account->msgid_with_addr) {
6042 addr = compose->account->address;
6044 generate_msgid(buf, sizeof(buf), addr);
6045 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6046 compose->msgid = g_strdup(buf);
6048 compose->msgid = NULL;
6051 if (compose->remove_references == FALSE) {
6053 if (compose->inreplyto && compose->to_list)
6054 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6057 if (compose->references)
6058 g_string_append_printf(header, "References: %s\n", compose->references);
6062 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6065 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6068 if (compose->account->organization &&
6069 strlen(compose->account->organization) &&
6070 !IS_IN_CUSTOM_HEADER("Organization")) {
6071 compose_convert_header(compose, buf, sizeof(buf),
6072 compose->account->organization,
6073 strlen("Organization: "), FALSE);
6074 g_string_append_printf(header, "Organization: %s\n", buf);
6077 /* Program version and system info */
6078 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6079 !compose->newsgroup_list) {
6080 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6082 gtk_major_version, gtk_minor_version, gtk_micro_version,
6085 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6086 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6088 gtk_major_version, gtk_minor_version, gtk_micro_version,
6092 /* custom headers */
6093 if (compose->account->add_customhdr) {
6096 for (cur = compose->account->customhdr_list; cur != NULL;
6098 CustomHeader *chdr = (CustomHeader *)cur->data;
6100 if (custom_header_is_allowed(chdr->name)) {
6101 compose_convert_header
6102 (compose, buf, sizeof(buf),
6103 chdr->value ? chdr->value : "",
6104 strlen(chdr->name) + 2, FALSE);
6105 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6110 /* Automatic Faces and X-Faces */
6111 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6112 g_string_append_printf(header, "X-Face: %s\n", buf);
6114 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6115 g_string_append_printf(header, "X-Face: %s\n", buf);
6117 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6118 g_string_append_printf(header, "Face: %s\n", buf);
6120 else if (get_default_face (buf, sizeof(buf)) == 0) {
6121 g_string_append_printf(header, "Face: %s\n", buf);
6125 switch (compose->priority) {
6126 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6127 "X-Priority: 1 (Highest)\n");
6129 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6130 "X-Priority: 2 (High)\n");
6132 case PRIORITY_NORMAL: break;
6133 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6134 "X-Priority: 4 (Low)\n");
6136 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6137 "X-Priority: 5 (Lowest)\n");
6139 default: debug_print("compose: priority unknown : %d\n",
6143 /* Request Return Receipt */
6144 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6145 if (compose->return_receipt) {
6146 if (compose->account->name
6147 && *compose->account->name) {
6148 compose_convert_header(compose, buf, sizeof(buf),
6149 compose->account->name,
6150 strlen("Disposition-Notification-To: "),
6152 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6154 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6158 /* get special headers */
6159 for (list = compose->header_list; list; list = list->next) {
6160 ComposeHeaderEntry *headerentry;
6163 gchar *headername_wcolon;
6164 const gchar *headername_trans;
6167 gboolean standard_header = FALSE;
6169 headerentry = ((ComposeHeaderEntry *)list->data);
6171 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6173 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6178 if (!strstr(tmp, ":")) {
6179 headername_wcolon = g_strconcat(tmp, ":", NULL);
6180 headername = g_strdup(tmp);
6182 headername_wcolon = g_strdup(tmp);
6183 headername = g_strdup(strtok(tmp, ":"));
6187 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6188 Xstrdup_a(headervalue, entry_str, return NULL);
6189 subst_char(headervalue, '\r', ' ');
6190 subst_char(headervalue, '\n', ' ');
6191 string = std_headers;
6192 while (*string != NULL) {
6193 headername_trans = prefs_common_translated_header_name(*string);
6194 if (!strcmp(headername_trans, headername_wcolon))
6195 standard_header = TRUE;
6198 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6199 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6202 g_free(headername_wcolon);
6206 g_string_free(header, FALSE);
6211 #undef IS_IN_CUSTOM_HEADER
6213 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6214 gint header_len, gboolean addr_field)
6216 gchar *tmpstr = NULL;
6217 const gchar *out_codeset = NULL;
6219 cm_return_if_fail(src != NULL);
6220 cm_return_if_fail(dest != NULL);
6222 if (len < 1) return;
6224 tmpstr = g_strdup(src);
6226 subst_char(tmpstr, '\n', ' ');
6227 subst_char(tmpstr, '\r', ' ');
6230 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6231 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6232 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6237 codeconv_set_strict(TRUE);
6238 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6239 conv_get_charset_str(compose->out_encoding));
6240 codeconv_set_strict(FALSE);
6242 if (!dest || *dest == '\0') {
6243 gchar *test_conv_global_out = NULL;
6244 gchar *test_conv_reply = NULL;
6246 /* automatic mode. be automatic. */
6247 codeconv_set_strict(TRUE);
6249 out_codeset = conv_get_outgoing_charset_str();
6251 debug_print("trying to convert to %s\n", out_codeset);
6252 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6255 if (!test_conv_global_out && compose->orig_charset
6256 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6257 out_codeset = compose->orig_charset;
6258 debug_print("failure; trying to convert to %s\n", out_codeset);
6259 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6262 if (!test_conv_global_out && !test_conv_reply) {
6264 out_codeset = CS_INTERNAL;
6265 debug_print("finally using %s\n", out_codeset);
6267 g_free(test_conv_global_out);
6268 g_free(test_conv_reply);
6269 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6271 codeconv_set_strict(FALSE);
6276 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6280 cm_return_if_fail(user_data != NULL);
6282 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6283 g_strstrip(address);
6284 if (*address != '\0') {
6285 gchar *name = procheader_get_fromname(address);
6286 extract_address(address);
6287 addressbook_add_contact(name, address, NULL, NULL);
6292 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6294 GtkWidget *menuitem;
6297 cm_return_if_fail(menu != NULL);
6298 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6300 menuitem = gtk_separator_menu_item_new();
6301 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6302 gtk_widget_show(menuitem);
6304 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6305 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6307 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6308 g_strstrip(address);
6309 if (*address == '\0') {
6310 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6313 g_signal_connect(G_OBJECT(menuitem), "activate",
6314 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6315 gtk_widget_show(menuitem);
6318 static void compose_create_header_entry(Compose *compose)
6320 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6327 const gchar *header = NULL;
6328 ComposeHeaderEntry *headerentry;
6329 gboolean standard_header = FALSE;
6330 #if !(GTK_CHECK_VERSION(2,12,0))
6331 GtkTooltips *tips = compose->tooltips;
6334 headerentry = g_new0(ComposeHeaderEntry, 1);
6337 combo = gtk_combo_box_entry_new_text();
6339 while(*string != NULL) {
6340 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6341 (gchar*)prefs_common_translated_header_name(*string));
6344 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6345 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6346 G_CALLBACK(compose_grab_focus_cb), compose);
6347 gtk_widget_show(combo);
6348 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6349 compose->header_nextrow, compose->header_nextrow+1,
6350 GTK_SHRINK, GTK_FILL, 0, 0);
6351 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6352 const gchar *last_header_entry = gtk_entry_get_text(
6353 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6355 while (*string != NULL) {
6356 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6357 standard_header = TRUE;
6360 if (standard_header)
6361 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6363 if (!compose->header_last || !standard_header) {
6364 switch(compose->account->protocol) {
6366 header = prefs_common_translated_header_name("Newsgroups:");
6369 header = prefs_common_translated_header_name("To:");
6374 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6376 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6377 G_CALLBACK(compose_grab_focus_cb), compose);
6379 /* Entry field with cleanup button */
6380 #if GTK_CHECK_VERSION(2, 8, 0)
6381 button = gtk_button_new();
6382 gtk_button_set_image(GTK_BUTTON(button),
6383 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6385 button = gtk_button_new_with_label(_("Clear"));
6387 gtk_widget_show(button);
6388 CLAWS_SET_TIP(button,
6389 _("Delete entry contents"));
6390 entry = gtk_entry_new();
6391 gtk_widget_show(entry);
6392 CLAWS_SET_TIP(entry,
6393 _("Use <tab> to autocomplete from addressbook"));
6394 hbox = gtk_hbox_new (FALSE, 0);
6395 gtk_widget_show(hbox);
6396 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6397 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6398 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6399 compose->header_nextrow, compose->header_nextrow+1,
6400 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6402 g_signal_connect(G_OBJECT(entry), "key-press-event",
6403 G_CALLBACK(compose_headerentry_key_press_event_cb),
6405 g_signal_connect(G_OBJECT(entry), "changed",
6406 G_CALLBACK(compose_headerentry_changed_cb),
6408 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6409 G_CALLBACK(compose_grab_focus_cb), compose);
6411 g_signal_connect(G_OBJECT(button), "clicked",
6412 G_CALLBACK(compose_headerentry_button_clicked_cb),
6416 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6417 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6418 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6419 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6420 G_CALLBACK(compose_header_drag_received_cb),
6422 g_signal_connect(G_OBJECT(entry), "drag-drop",
6423 G_CALLBACK(compose_drag_drop),
6425 g_signal_connect(G_OBJECT(entry), "populate-popup",
6426 G_CALLBACK(compose_entry_popup_extend),
6429 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6431 headerentry->compose = compose;
6432 headerentry->combo = combo;
6433 headerentry->entry = entry;
6434 headerentry->button = button;
6435 headerentry->hbox = hbox;
6436 headerentry->headernum = compose->header_nextrow;
6438 compose->header_nextrow++;
6439 compose->header_last = headerentry;
6440 compose->header_list =
6441 g_slist_append(compose->header_list,
6445 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6447 ComposeHeaderEntry *last_header;
6449 last_header = compose->header_last;
6451 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6452 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6455 static void compose_remove_header_entries(Compose *compose)
6458 for (list = compose->header_list; list; list = list->next) {
6459 ComposeHeaderEntry *headerentry =
6460 (ComposeHeaderEntry *)list->data;
6461 gtk_widget_destroy(headerentry->combo);
6462 gtk_widget_destroy(headerentry->entry);
6463 gtk_widget_destroy(headerentry->button);
6464 gtk_widget_destroy(headerentry->hbox);
6465 g_free(headerentry);
6467 compose->header_last = NULL;
6468 g_slist_free(compose->header_list);
6469 compose->header_list = NULL;
6470 compose->header_nextrow = 1;
6471 compose_create_header_entry(compose);
6474 static GtkWidget *compose_create_header(Compose *compose)
6476 GtkWidget *from_optmenu_hbox;
6477 GtkWidget *header_scrolledwin;
6478 GtkWidget *header_table;
6482 /* header labels and entries */
6483 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6484 gtk_widget_show(header_scrolledwin);
6485 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6487 header_table = gtk_table_new(2, 2, FALSE);
6488 gtk_widget_show(header_table);
6489 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6490 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6491 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6494 /* option menu for selecting accounts */
6495 from_optmenu_hbox = compose_account_option_menu_create(compose);
6496 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6497 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6500 compose->header_table = header_table;
6501 compose->header_list = NULL;
6502 compose->header_nextrow = count;
6504 compose_create_header_entry(compose);
6506 compose->table = NULL;
6508 return header_scrolledwin ;
6511 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6513 Compose *compose = (Compose *)data;
6514 GdkEventButton event;
6517 event.time = gtk_get_current_event_time();
6519 return attach_button_pressed(compose->attach_clist, &event, compose);
6522 static GtkWidget *compose_create_attach(Compose *compose)
6524 GtkWidget *attach_scrwin;
6525 GtkWidget *attach_clist;
6527 GtkListStore *store;
6528 GtkCellRenderer *renderer;
6529 GtkTreeViewColumn *column;
6530 GtkTreeSelection *selection;
6532 /* attachment list */
6533 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6534 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6535 GTK_POLICY_AUTOMATIC,
6536 GTK_POLICY_AUTOMATIC);
6537 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6539 store = gtk_list_store_new(N_ATTACH_COLS,
6544 G_TYPE_AUTO_POINTER,
6546 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6547 (GTK_TREE_MODEL(store)));
6548 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6549 g_object_unref(store);
6551 renderer = gtk_cell_renderer_text_new();
6552 column = gtk_tree_view_column_new_with_attributes
6553 (_("Mime type"), renderer, "text",
6554 COL_MIMETYPE, NULL);
6555 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6557 renderer = gtk_cell_renderer_text_new();
6558 column = gtk_tree_view_column_new_with_attributes
6559 (_("Size"), renderer, "text",
6561 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6563 renderer = gtk_cell_renderer_text_new();
6564 column = gtk_tree_view_column_new_with_attributes
6565 (_("Name"), renderer, "text",
6567 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6569 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6570 prefs_common.use_stripes_everywhere);
6571 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6572 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6574 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6575 G_CALLBACK(attach_selected), compose);
6576 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6577 G_CALLBACK(attach_button_pressed), compose);
6579 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6580 G_CALLBACK(popup_attach_button_pressed), compose);
6582 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6583 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6584 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6585 G_CALLBACK(popup_attach_button_pressed), compose);
6587 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6588 G_CALLBACK(attach_key_pressed), compose);
6591 gtk_drag_dest_set(attach_clist,
6592 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6593 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6594 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6595 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6596 G_CALLBACK(compose_attach_drag_received_cb),
6598 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6599 G_CALLBACK(compose_drag_drop),
6602 compose->attach_scrwin = attach_scrwin;
6603 compose->attach_clist = attach_clist;
6605 return attach_scrwin;
6608 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6609 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6611 static GtkWidget *compose_create_others(Compose *compose)
6614 GtkWidget *savemsg_checkbtn;
6615 GtkWidget *savemsg_combo;
6616 GtkWidget *savemsg_select;
6619 gchar *folderidentifier;
6621 /* Table for settings */
6622 table = gtk_table_new(3, 1, FALSE);
6623 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6624 gtk_widget_show(table);
6625 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6628 /* Save Message to folder */
6629 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6630 gtk_widget_show(savemsg_checkbtn);
6631 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6632 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6633 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6635 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6636 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6638 savemsg_combo = gtk_combo_box_entry_new_text();
6639 compose->savemsg_checkbtn = savemsg_checkbtn;
6640 compose->savemsg_combo = savemsg_combo;
6641 gtk_widget_show(savemsg_combo);
6643 if (prefs_common.compose_save_to_history)
6644 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6645 prefs_common.compose_save_to_history);
6647 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6648 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6649 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6650 G_CALLBACK(compose_grab_focus_cb), compose);
6651 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6652 folderidentifier = folder_item_get_identifier(account_get_special_folder
6653 (compose->account, F_OUTBOX));
6654 compose_set_save_to(compose, folderidentifier);
6655 g_free(folderidentifier);
6658 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6659 gtk_widget_show(savemsg_select);
6660 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6661 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6662 G_CALLBACK(compose_savemsg_select_cb),
6670 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6672 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6673 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6676 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6681 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6684 path = folder_item_get_identifier(dest);
6686 compose_set_save_to(compose, path);
6690 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6691 GdkAtom clip, GtkTextIter *insert_place);
6694 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6698 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6700 if (event->button == 3) {
6702 GtkTextIter sel_start, sel_end;
6703 gboolean stuff_selected;
6705 /* move the cursor to allow GtkAspell to check the word
6706 * under the mouse */
6707 if (event->x && event->y) {
6708 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6709 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6711 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6714 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6715 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6718 stuff_selected = gtk_text_buffer_get_selection_bounds(
6720 &sel_start, &sel_end);
6722 gtk_text_buffer_place_cursor (buffer, &iter);
6723 /* reselect stuff */
6725 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6726 gtk_text_buffer_select_range(buffer,
6727 &sel_start, &sel_end);
6729 return FALSE; /* pass the event so that the right-click goes through */
6732 if (event->button == 2) {
6737 /* get the middle-click position to paste at the correct place */
6738 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6739 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6741 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6744 entry_paste_clipboard(compose, text,
6745 prefs_common.linewrap_pastes,
6746 GDK_SELECTION_PRIMARY, &iter);
6754 static void compose_spell_menu_changed(void *data)
6756 Compose *compose = (Compose *)data;
6758 GtkWidget *menuitem;
6759 GtkWidget *parent_item;
6760 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6763 if (compose->gtkaspell == NULL)
6766 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6767 "/Menu/Spelling/Options");
6769 /* setting the submenu removes /Spelling/Options from the factory
6770 * so we need to save it */
6772 if (parent_item == NULL) {
6773 parent_item = compose->aspell_options_menu;
6774 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6776 compose->aspell_options_menu = parent_item;
6778 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6780 spell_menu = g_slist_reverse(spell_menu);
6781 for (items = spell_menu;
6782 items; items = items->next) {
6783 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6784 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6785 gtk_widget_show(GTK_WIDGET(menuitem));
6787 g_slist_free(spell_menu);
6789 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6790 gtk_widget_show(parent_item);
6793 static void compose_dict_changed(void *data)
6795 Compose *compose = (Compose *) data;
6797 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6800 gtkaspell_highlight_all(compose->gtkaspell);
6801 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6805 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6807 Compose *compose = (Compose *)data;
6808 GdkEventButton event;
6811 event.time = gtk_get_current_event_time();
6815 return text_clicked(compose->text, &event, compose);
6818 static gboolean compose_force_window_origin = TRUE;
6819 static Compose *compose_create(PrefsAccount *account,
6828 GtkWidget *handlebox;
6830 GtkWidget *notebook;
6832 GtkWidget *attach_hbox;
6833 GtkWidget *attach_lab1;
6834 GtkWidget *attach_lab2;
6839 GtkWidget *subject_hbox;
6840 GtkWidget *subject_frame;
6841 GtkWidget *subject_entry;
6845 GtkWidget *edit_vbox;
6846 GtkWidget *ruler_hbox;
6848 GtkWidget *scrolledwin;
6850 GtkTextBuffer *buffer;
6851 GtkClipboard *clipboard;
6854 UndoMain *undostruct;
6856 gchar *titles[N_ATTACH_COLS];
6857 GtkWidget *popupmenu;
6858 GtkWidget *tmpl_menu;
6859 GtkActionGroup *action_group = NULL;
6862 GtkAspell * gtkaspell = NULL;
6865 static GdkGeometry geometry;
6867 cm_return_val_if_fail(account != NULL, NULL);
6869 debug_print("Creating compose window...\n");
6870 compose = g_new0(Compose, 1);
6872 titles[COL_MIMETYPE] = _("MIME type");
6873 titles[COL_SIZE] = _("Size");
6874 titles[COL_NAME] = _("Name");
6876 compose->batch = batch;
6877 compose->account = account;
6878 compose->folder = folder;
6880 compose->mutex = g_mutex_new();
6881 compose->set_cursor_pos = -1;
6883 #if !(GTK_CHECK_VERSION(2,12,0))
6884 compose->tooltips = tips;
6887 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6889 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6890 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6892 if (!geometry.max_width) {
6893 geometry.max_width = gdk_screen_width();
6894 geometry.max_height = gdk_screen_height();
6897 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6898 &geometry, GDK_HINT_MAX_SIZE);
6899 if (!geometry.min_width) {
6900 geometry.min_width = 600;
6901 geometry.min_height = 440;
6903 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6904 &geometry, GDK_HINT_MIN_SIZE);
6906 #ifndef GENERIC_UMPC
6907 if (compose_force_window_origin)
6908 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6909 prefs_common.compose_y);
6911 g_signal_connect(G_OBJECT(window), "delete_event",
6912 G_CALLBACK(compose_delete_cb), compose);
6913 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6914 gtk_widget_realize(window);
6916 gtkut_widget_set_composer_icon(window);
6918 vbox = gtk_vbox_new(FALSE, 0);
6919 gtk_container_add(GTK_CONTAINER(window), vbox);
6921 compose->ui_manager = gtk_ui_manager_new();
6922 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6923 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6924 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6925 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6926 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6927 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6928 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6929 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6930 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6931 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6988 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7002 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7010 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7030 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7051 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)
7052 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)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7058 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)
7059 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)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7064 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)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7068 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)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7074 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)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7080 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)
7081 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)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7093 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)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7096 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7097 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7109 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7111 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7112 gtk_widget_show_all(menubar);
7114 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7116 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7118 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7121 if (prefs_common.toolbar_detachable) {
7122 handlebox = gtk_handle_box_new();
7124 handlebox = gtk_hbox_new(FALSE, 0);
7126 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7128 gtk_widget_realize(handlebox);
7130 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7133 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7137 vbox2 = gtk_vbox_new(FALSE, 2);
7138 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7139 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7142 notebook = gtk_notebook_new();
7143 gtk_widget_set_size_request(notebook, -1, 130);
7144 gtk_widget_show(notebook);
7146 /* header labels and entries */
7147 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7148 compose_create_header(compose),
7149 gtk_label_new_with_mnemonic(_("Hea_der")));
7150 /* attachment list */
7151 attach_hbox = gtk_hbox_new(FALSE, 0);
7152 gtk_widget_show(attach_hbox);
7154 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7155 gtk_widget_show(attach_lab1);
7156 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7158 attach_lab2 = gtk_label_new("");
7159 gtk_widget_show(attach_lab2);
7160 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7162 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7163 compose_create_attach(compose),
7166 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7167 compose_create_others(compose),
7168 gtk_label_new_with_mnemonic(_("Othe_rs")));
7171 subject_hbox = gtk_hbox_new(FALSE, 0);
7172 gtk_widget_show(subject_hbox);
7174 subject_frame = gtk_frame_new(NULL);
7175 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7176 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7177 gtk_widget_show(subject_frame);
7179 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7180 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7181 gtk_widget_show(subject);
7183 label = gtk_label_new(_("Subject:"));
7184 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7185 gtk_widget_show(label);
7188 subject_entry = claws_spell_entry_new();
7190 subject_entry = gtk_entry_new();
7192 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7193 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7194 G_CALLBACK(compose_grab_focus_cb), compose);
7195 gtk_widget_show(subject_entry);
7196 compose->subject_entry = subject_entry;
7197 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7199 edit_vbox = gtk_vbox_new(FALSE, 0);
7201 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7204 ruler_hbox = gtk_hbox_new(FALSE, 0);
7205 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7207 ruler = gtk_shruler_new();
7208 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7209 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7213 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7214 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7215 GTK_POLICY_AUTOMATIC,
7216 GTK_POLICY_AUTOMATIC);
7217 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7219 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7220 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7222 text = gtk_text_view_new();
7223 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7224 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7225 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7226 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7227 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7229 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7231 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7232 G_CALLBACK(compose_edit_size_alloc),
7234 g_signal_connect(G_OBJECT(buffer), "changed",
7235 G_CALLBACK(compose_changed_cb), compose);
7236 g_signal_connect(G_OBJECT(text), "grab_focus",
7237 G_CALLBACK(compose_grab_focus_cb), compose);
7238 g_signal_connect(G_OBJECT(buffer), "insert_text",
7239 G_CALLBACK(text_inserted), compose);
7240 g_signal_connect(G_OBJECT(text), "button_press_event",
7241 G_CALLBACK(text_clicked), compose);
7243 g_signal_connect(G_OBJECT(text), "popup-menu",
7244 G_CALLBACK(compose_popup_menu), compose);
7246 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7247 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7248 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7249 G_CALLBACK(compose_popup_menu), compose);
7251 g_signal_connect(G_OBJECT(subject_entry), "changed",
7252 G_CALLBACK(compose_changed_cb), compose);
7255 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7256 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7257 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7258 g_signal_connect(G_OBJECT(text), "drag_data_received",
7259 G_CALLBACK(compose_insert_drag_received_cb),
7261 g_signal_connect(G_OBJECT(text), "drag-drop",
7262 G_CALLBACK(compose_drag_drop),
7264 gtk_widget_show_all(vbox);
7266 /* pane between attach clist and text */
7267 paned = gtk_vpaned_new();
7268 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7270 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7271 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7273 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7275 gtk_paned_add1(GTK_PANED(paned), notebook);
7276 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7277 gtk_widget_show_all(paned);
7280 if (prefs_common.textfont) {
7281 PangoFontDescription *font_desc;
7283 font_desc = pango_font_description_from_string
7284 (prefs_common.textfont);
7286 gtk_widget_modify_font(text, font_desc);
7287 pango_font_description_free(font_desc);
7291 gtk_action_group_add_actions(action_group, compose_popup_entries,
7292 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7300 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7302 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7303 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7304 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7306 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7308 undostruct = undo_init(text);
7309 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7312 address_completion_start(window);
7314 compose->window = window;
7315 compose->vbox = vbox;
7316 compose->menubar = menubar;
7317 compose->handlebox = handlebox;
7319 compose->vbox2 = vbox2;
7321 compose->paned = paned;
7323 compose->attach_label = attach_lab2;
7325 compose->notebook = notebook;
7326 compose->edit_vbox = edit_vbox;
7327 compose->ruler_hbox = ruler_hbox;
7328 compose->ruler = ruler;
7329 compose->scrolledwin = scrolledwin;
7330 compose->text = text;
7332 compose->focused_editable = NULL;
7334 compose->popupmenu = popupmenu;
7336 compose->tmpl_menu = tmpl_menu;
7338 compose->mode = mode;
7339 compose->rmode = mode;
7341 compose->targetinfo = NULL;
7342 compose->replyinfo = NULL;
7343 compose->fwdinfo = NULL;
7345 compose->replyto = NULL;
7347 compose->bcc = NULL;
7348 compose->followup_to = NULL;
7350 compose->ml_post = NULL;
7352 compose->inreplyto = NULL;
7353 compose->references = NULL;
7354 compose->msgid = NULL;
7355 compose->boundary = NULL;
7357 compose->autowrap = prefs_common.autowrap;
7358 compose->autoindent = prefs_common.auto_indent;
7359 compose->use_signing = FALSE;
7360 compose->use_encryption = FALSE;
7361 compose->privacy_system = NULL;
7363 compose->modified = FALSE;
7365 compose->return_receipt = FALSE;
7367 compose->to_list = NULL;
7368 compose->newsgroup_list = NULL;
7370 compose->undostruct = undostruct;
7372 compose->sig_str = NULL;
7374 compose->exteditor_file = NULL;
7375 compose->exteditor_pid = -1;
7376 compose->exteditor_tag = -1;
7377 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7380 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7381 if (mode != COMPOSE_REDIRECT) {
7382 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7383 strcmp(prefs_common.dictionary, "")) {
7384 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7385 prefs_common.alt_dictionary,
7386 conv_get_locale_charset_str(),
7387 prefs_common.misspelled_col,
7388 prefs_common.check_while_typing,
7389 prefs_common.recheck_when_changing_dict,
7390 prefs_common.use_alternate,
7391 prefs_common.use_both_dicts,
7392 GTK_TEXT_VIEW(text),
7393 GTK_WINDOW(compose->window),
7394 compose_dict_changed,
7395 compose_spell_menu_changed,
7398 alertpanel_error(_("Spell checker could not "
7400 gtkaspell_checkers_strerror());
7401 gtkaspell_checkers_reset_error();
7403 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7407 compose->gtkaspell = gtkaspell;
7408 compose_spell_menu_changed(compose);
7409 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7412 compose_select_account(compose, account, TRUE);
7414 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7415 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7417 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7418 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7420 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7421 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7423 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7424 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7427 if (account->protocol != A_NNTP)
7428 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7429 prefs_common_translated_header_name("To:"));
7431 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7432 prefs_common_translated_header_name("Newsgroups:"));
7434 addressbook_set_target_compose(compose);
7436 if (mode != COMPOSE_REDIRECT)
7437 compose_set_template_menu(compose);
7439 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7442 compose_list = g_list_append(compose_list, compose);
7444 if (!prefs_common.show_ruler)
7445 gtk_widget_hide(ruler_hbox);
7447 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7450 compose->priority = PRIORITY_NORMAL;
7451 compose_update_priority_menu_item(compose);
7453 compose_set_out_encoding(compose);
7456 compose_update_actions_menu(compose);
7458 /* Privacy Systems menu */
7459 compose_update_privacy_systems_menu(compose);
7461 activate_privacy_system(compose, account, TRUE);
7462 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7464 gtk_widget_realize(window);
7466 gtk_widget_show(window);
7468 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7469 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7476 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7481 GtkWidget *optmenubox;
7484 GtkWidget *from_name = NULL;
7485 #if !(GTK_CHECK_VERSION(2,12,0))
7486 GtkTooltips *tips = compose->tooltips;
7489 gint num = 0, def_menu = 0;
7491 accounts = account_get_list();
7492 cm_return_val_if_fail(accounts != NULL, NULL);
7494 optmenubox = gtk_event_box_new();
7495 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7496 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7498 hbox = gtk_hbox_new(FALSE, 6);
7499 from_name = gtk_entry_new();
7501 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7502 G_CALLBACK(compose_grab_focus_cb), compose);
7504 for (; accounts != NULL; accounts = accounts->next, num++) {
7505 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7506 gchar *name, *from = NULL;
7508 if (ac == compose->account) def_menu = num;
7510 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7513 if (ac == compose->account) {
7514 if (ac->name && *ac->name) {
7516 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7517 from = g_strdup_printf("%s <%s>",
7519 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7521 from = g_strdup_printf("%s",
7523 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7526 COMBOBOX_ADD(menu, name, ac->account_id);
7531 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7533 g_signal_connect(G_OBJECT(optmenu), "changed",
7534 G_CALLBACK(account_activated),
7536 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7537 G_CALLBACK(compose_entry_popup_extend),
7540 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7541 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7543 CLAWS_SET_TIP(optmenubox,
7544 _("Account to use for this email"));
7545 CLAWS_SET_TIP(from_name,
7546 _("Sender address to be used"));
7548 compose->from_name = from_name;
7553 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7555 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7556 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7557 Compose *compose = (Compose *) data;
7559 compose->priority = value;
7563 static void compose_reply_change_mode(Compose *compose,
7566 gboolean was_modified = compose->modified;
7568 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7570 cm_return_if_fail(compose->replyinfo != NULL);
7572 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7574 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7576 if (action == COMPOSE_REPLY_TO_ALL)
7578 if (action == COMPOSE_REPLY_TO_SENDER)
7580 if (action == COMPOSE_REPLY_TO_LIST)
7583 compose_remove_header_entries(compose);
7584 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7585 if (compose->account->set_autocc && compose->account->auto_cc)
7586 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7588 if (compose->account->set_autobcc && compose->account->auto_bcc)
7589 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7591 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7592 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7593 compose_show_first_last_header(compose, TRUE);
7594 compose->modified = was_modified;
7595 compose_set_title(compose);
7598 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7600 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7601 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7602 Compose *compose = (Compose *) data;
7605 compose_reply_change_mode(compose, value);
7608 static void compose_update_priority_menu_item(Compose * compose)
7610 GtkWidget *menuitem = NULL;
7611 switch (compose->priority) {
7612 case PRIORITY_HIGHEST:
7613 menuitem = gtk_ui_manager_get_widget
7614 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7617 menuitem = gtk_ui_manager_get_widget
7618 (compose->ui_manager, "/Menu/Options/Priority/High");
7620 case PRIORITY_NORMAL:
7621 menuitem = gtk_ui_manager_get_widget
7622 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7625 menuitem = gtk_ui_manager_get_widget
7626 (compose->ui_manager, "/Menu/Options/Priority/Low");
7628 case PRIORITY_LOWEST:
7629 menuitem = gtk_ui_manager_get_widget
7630 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7633 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7636 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7638 Compose *compose = (Compose *) data;
7640 gboolean can_sign = FALSE, can_encrypt = FALSE;
7642 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7644 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7647 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7648 g_free(compose->privacy_system);
7649 compose->privacy_system = NULL;
7650 if (systemid != NULL) {
7651 compose->privacy_system = g_strdup(systemid);
7653 can_sign = privacy_system_can_sign(systemid);
7654 can_encrypt = privacy_system_can_encrypt(systemid);
7657 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7659 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7660 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7663 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7665 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7666 GtkWidget *menuitem = NULL;
7668 gboolean can_sign = FALSE, can_encrypt = FALSE;
7669 gboolean found = FALSE;
7671 if (compose->privacy_system != NULL) {
7673 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7674 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7675 cm_return_if_fail(menuitem != NULL);
7677 amenu = GTK_MENU_SHELL(menuitem)->children;
7679 while (amenu != NULL) {
7680 GList *alist = amenu->next;
7682 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7683 if (systemid != NULL) {
7684 if (strcmp(systemid, compose->privacy_system) == 0 &&
7685 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7686 menuitem = GTK_WIDGET(amenu->data);
7688 can_sign = privacy_system_can_sign(systemid);
7689 can_encrypt = privacy_system_can_encrypt(systemid);
7693 } else if (strlen(compose->privacy_system) == 0 &&
7694 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7695 menuitem = GTK_WIDGET(amenu->data);
7698 can_encrypt = FALSE;
7705 if (menuitem != NULL)
7706 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7708 if (warn && !found && strlen(compose->privacy_system)) {
7709 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7710 "will not be able to sign or encrypt this message."),
7711 compose->privacy_system);
7715 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7716 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7719 static void compose_set_out_encoding(Compose *compose)
7721 CharSet out_encoding;
7722 const gchar *branch = NULL;
7723 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7725 switch(out_encoding) {
7726 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7727 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7728 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7729 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7730 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7731 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7732 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7733 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7734 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7735 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7736 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7737 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7738 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7739 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7740 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7741 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7742 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7743 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7744 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7745 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7746 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7747 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7748 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7749 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7750 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7751 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7752 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7753 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7754 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7755 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7756 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7757 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7759 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7762 static void compose_set_template_menu(Compose *compose)
7764 GSList *tmpl_list, *cur;
7768 tmpl_list = template_get_config();
7770 menu = gtk_menu_new();
7772 gtk_menu_set_accel_group (GTK_MENU (menu),
7773 gtk_ui_manager_get_accel_group(compose->ui_manager));
7774 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7775 Template *tmpl = (Template *)cur->data;
7776 gchar *accel_path = NULL;
7777 item = gtk_menu_item_new_with_label(tmpl->name);
7778 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7779 g_signal_connect(G_OBJECT(item), "activate",
7780 G_CALLBACK(compose_template_activate_cb),
7782 g_object_set_data(G_OBJECT(item), "template", tmpl);
7783 gtk_widget_show(item);
7784 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7785 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7789 gtk_widget_show(menu);
7790 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7793 void compose_update_actions_menu(Compose *compose)
7795 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7798 static void compose_update_privacy_systems_menu(Compose *compose)
7800 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7801 GSList *systems, *cur;
7803 GtkWidget *system_none;
7805 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7806 GtkWidget *privacy_menu = gtk_menu_new();
7808 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7809 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7811 g_signal_connect(G_OBJECT(system_none), "activate",
7812 G_CALLBACK(compose_set_privacy_system_cb), compose);
7814 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7815 gtk_widget_show(system_none);
7817 systems = privacy_get_system_ids();
7818 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7819 gchar *systemid = cur->data;
7821 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7822 widget = gtk_radio_menu_item_new_with_label(group,
7823 privacy_system_get_name(systemid));
7824 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7825 g_strdup(systemid), g_free);
7826 g_signal_connect(G_OBJECT(widget), "activate",
7827 G_CALLBACK(compose_set_privacy_system_cb), compose);
7829 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7830 gtk_widget_show(widget);
7833 g_slist_free(systems);
7834 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7835 gtk_widget_show_all(privacy_menu);
7836 gtk_widget_show_all(privacy_menuitem);
7839 void compose_reflect_prefs_all(void)
7844 for (cur = compose_list; cur != NULL; cur = cur->next) {
7845 compose = (Compose *)cur->data;
7846 compose_set_template_menu(compose);
7850 void compose_reflect_prefs_pixmap_theme(void)
7855 for (cur = compose_list; cur != NULL; cur = cur->next) {
7856 compose = (Compose *)cur->data;
7857 toolbar_update(TOOLBAR_COMPOSE, compose);
7861 static const gchar *compose_quote_char_from_context(Compose *compose)
7863 const gchar *qmark = NULL;
7865 cm_return_val_if_fail(compose != NULL, NULL);
7867 switch (compose->mode) {
7868 /* use forward-specific quote char */
7869 case COMPOSE_FORWARD:
7870 case COMPOSE_FORWARD_AS_ATTACH:
7871 case COMPOSE_FORWARD_INLINE:
7872 if (compose->folder && compose->folder->prefs &&
7873 compose->folder->prefs->forward_with_format)
7874 qmark = compose->folder->prefs->forward_quotemark;
7875 else if (compose->account->forward_with_format)
7876 qmark = compose->account->forward_quotemark;
7878 qmark = prefs_common.fw_quotemark;
7881 /* use reply-specific quote char in all other modes */
7883 if (compose->folder && compose->folder->prefs &&
7884 compose->folder->prefs->reply_with_format)
7885 qmark = compose->folder->prefs->reply_quotemark;
7886 else if (compose->account->reply_with_format)
7887 qmark = compose->account->reply_quotemark;
7889 qmark = prefs_common.quotemark;
7893 if (qmark == NULL || *qmark == '\0')
7899 static void compose_template_apply(Compose *compose, Template *tmpl,
7903 GtkTextBuffer *buffer;
7907 gchar *parsed_str = NULL;
7908 gint cursor_pos = 0;
7909 const gchar *err_msg = _("The body of the template has an error at line %d.");
7912 /* process the body */
7914 text = GTK_TEXT_VIEW(compose->text);
7915 buffer = gtk_text_view_get_buffer(text);
7918 qmark = compose_quote_char_from_context(compose);
7920 if (compose->replyinfo != NULL) {
7923 gtk_text_buffer_set_text(buffer, "", -1);
7924 mark = gtk_text_buffer_get_insert(buffer);
7925 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7927 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7928 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7930 } else if (compose->fwdinfo != NULL) {
7933 gtk_text_buffer_set_text(buffer, "", -1);
7934 mark = gtk_text_buffer_get_insert(buffer);
7935 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7937 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7938 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7941 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7943 GtkTextIter start, end;
7946 gtk_text_buffer_get_start_iter(buffer, &start);
7947 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7948 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7950 /* clear the buffer now */
7952 gtk_text_buffer_set_text(buffer, "", -1);
7954 parsed_str = compose_quote_fmt(compose, dummyinfo,
7955 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7956 procmsg_msginfo_free( dummyinfo );
7962 gtk_text_buffer_set_text(buffer, "", -1);
7963 mark = gtk_text_buffer_get_insert(buffer);
7964 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7967 if (replace && parsed_str && compose->account->auto_sig)
7968 compose_insert_sig(compose, FALSE);
7970 if (replace && parsed_str) {
7971 gtk_text_buffer_get_start_iter(buffer, &iter);
7972 gtk_text_buffer_place_cursor(buffer, &iter);
7976 cursor_pos = quote_fmt_get_cursor_pos();
7977 compose->set_cursor_pos = cursor_pos;
7978 if (cursor_pos == -1)
7980 gtk_text_buffer_get_start_iter(buffer, &iter);
7981 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7982 gtk_text_buffer_place_cursor(buffer, &iter);
7985 /* process the other fields */
7987 compose_template_apply_fields(compose, tmpl);
7988 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7989 quote_fmt_reset_vartable();
7990 compose_changed_cb(NULL, compose);
7993 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7995 MsgInfo* dummyinfo = NULL;
7996 MsgInfo *msginfo = NULL;
7999 if (compose->replyinfo != NULL)
8000 msginfo = compose->replyinfo;
8001 else if (compose->fwdinfo != NULL)
8002 msginfo = compose->fwdinfo;
8004 dummyinfo = compose_msginfo_new_from_compose(compose);
8005 msginfo = dummyinfo;
8008 if (tmpl->from && *tmpl->from != '\0') {
8010 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8011 compose->gtkaspell);
8013 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8015 quote_fmt_scan_string(tmpl->from);
8018 buf = quote_fmt_get_buffer();
8020 alertpanel_error(_("Template From format error."));
8022 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8026 if (tmpl->to && *tmpl->to != '\0') {
8028 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8029 compose->gtkaspell);
8031 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8033 quote_fmt_scan_string(tmpl->to);
8036 buf = quote_fmt_get_buffer();
8038 alertpanel_error(_("Template To format error."));
8040 compose_entry_append(compose, buf, COMPOSE_TO);
8044 if (tmpl->cc && *tmpl->cc != '\0') {
8046 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8047 compose->gtkaspell);
8049 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8051 quote_fmt_scan_string(tmpl->cc);
8054 buf = quote_fmt_get_buffer();
8056 alertpanel_error(_("Template Cc format error."));
8058 compose_entry_append(compose, buf, COMPOSE_CC);
8062 if (tmpl->bcc && *tmpl->bcc != '\0') {
8064 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8065 compose->gtkaspell);
8067 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8069 quote_fmt_scan_string(tmpl->bcc);
8072 buf = quote_fmt_get_buffer();
8074 alertpanel_error(_("Template Bcc format error."));
8076 compose_entry_append(compose, buf, COMPOSE_BCC);
8080 /* process the subject */
8081 if (tmpl->subject && *tmpl->subject != '\0') {
8083 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8084 compose->gtkaspell);
8086 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8088 quote_fmt_scan_string(tmpl->subject);
8091 buf = quote_fmt_get_buffer();
8093 alertpanel_error(_("Template subject format error."));
8095 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8099 procmsg_msginfo_free( dummyinfo );
8102 static void compose_destroy(Compose *compose)
8104 GtkTextBuffer *buffer;
8105 GtkClipboard *clipboard;
8107 compose_list = g_list_remove(compose_list, compose);
8109 if (compose->updating) {
8110 debug_print("danger, not destroying anything now\n");
8111 compose->deferred_destroy = TRUE;
8114 /* NOTE: address_completion_end() does nothing with the window
8115 * however this may change. */
8116 address_completion_end(compose->window);
8118 slist_free_strings(compose->to_list);
8119 g_slist_free(compose->to_list);
8120 slist_free_strings(compose->newsgroup_list);
8121 g_slist_free(compose->newsgroup_list);
8122 slist_free_strings(compose->header_list);
8123 g_slist_free(compose->header_list);
8125 procmsg_msginfo_free(compose->targetinfo);
8126 procmsg_msginfo_free(compose->replyinfo);
8127 procmsg_msginfo_free(compose->fwdinfo);
8129 g_free(compose->replyto);
8130 g_free(compose->cc);
8131 g_free(compose->bcc);
8132 g_free(compose->newsgroups);
8133 g_free(compose->followup_to);
8135 g_free(compose->ml_post);
8137 g_free(compose->inreplyto);
8138 g_free(compose->references);
8139 g_free(compose->msgid);
8140 g_free(compose->boundary);
8142 g_free(compose->redirect_filename);
8143 if (compose->undostruct)
8144 undo_destroy(compose->undostruct);
8146 g_free(compose->sig_str);
8148 g_free(compose->exteditor_file);
8150 g_free(compose->orig_charset);
8152 g_free(compose->privacy_system);
8154 if (addressbook_get_target_compose() == compose)
8155 addressbook_set_target_compose(NULL);
8158 if (compose->gtkaspell) {
8159 gtkaspell_delete(compose->gtkaspell);
8160 compose->gtkaspell = NULL;
8164 if (!compose->batch) {
8165 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8166 prefs_common.compose_height = compose->window->allocation.height;
8169 if (!gtk_widget_get_parent(compose->paned))
8170 gtk_widget_destroy(compose->paned);
8171 gtk_widget_destroy(compose->popupmenu);
8173 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8174 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8175 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8177 gtk_widget_destroy(compose->window);
8178 toolbar_destroy(compose->toolbar);
8179 g_free(compose->toolbar);
8180 g_mutex_free(compose->mutex);
8184 static void compose_attach_info_free(AttachInfo *ainfo)
8186 g_free(ainfo->file);
8187 g_free(ainfo->content_type);
8188 g_free(ainfo->name);
8192 static void compose_attach_update_label(Compose *compose)
8197 GtkTreeModel *model;
8202 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8203 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8204 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8208 while(gtk_tree_model_iter_next(model, &iter))
8211 text = g_strdup_printf("(%d)", i);
8212 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8216 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8218 Compose *compose = (Compose *)data;
8219 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8220 GtkTreeSelection *selection;
8222 GtkTreeModel *model;
8224 selection = gtk_tree_view_get_selection(tree_view);
8225 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8230 for (cur = sel; cur != NULL; cur = cur->next) {
8231 GtkTreePath *path = cur->data;
8232 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8235 gtk_tree_path_free(path);
8238 for (cur = sel; cur != NULL; cur = cur->next) {
8239 GtkTreeRowReference *ref = cur->data;
8240 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8243 if (gtk_tree_model_get_iter(model, &iter, path))
8244 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8246 gtk_tree_path_free(path);
8247 gtk_tree_row_reference_free(ref);
8251 compose_attach_update_label(compose);
8254 static struct _AttachProperty
8257 GtkWidget *mimetype_entry;
8258 GtkWidget *encoding_optmenu;
8259 GtkWidget *path_entry;
8260 GtkWidget *filename_entry;
8262 GtkWidget *cancel_btn;
8265 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8267 gtk_tree_path_free((GtkTreePath *)ptr);
8270 static void compose_attach_property(GtkAction *action, gpointer data)
8272 Compose *compose = (Compose *)data;
8273 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8275 GtkComboBox *optmenu;
8276 GtkTreeSelection *selection;
8278 GtkTreeModel *model;
8281 static gboolean cancelled;
8283 /* only if one selected */
8284 selection = gtk_tree_view_get_selection(tree_view);
8285 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8288 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8292 path = (GtkTreePath *) sel->data;
8293 gtk_tree_model_get_iter(model, &iter, path);
8294 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8297 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8303 if (!attach_prop.window)
8304 compose_attach_property_create(&cancelled);
8305 gtk_widget_grab_focus(attach_prop.ok_btn);
8306 gtk_widget_show(attach_prop.window);
8307 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8309 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8310 if (ainfo->encoding == ENC_UNKNOWN)
8311 combobox_select_by_data(optmenu, ENC_BASE64);
8313 combobox_select_by_data(optmenu, ainfo->encoding);
8315 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8316 ainfo->content_type ? ainfo->content_type : "");
8317 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8318 ainfo->file ? ainfo->file : "");
8319 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8320 ainfo->name ? ainfo->name : "");
8323 const gchar *entry_text;
8325 gchar *cnttype = NULL;
8332 gtk_widget_hide(attach_prop.window);
8337 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8338 if (*entry_text != '\0') {
8341 text = g_strstrip(g_strdup(entry_text));
8342 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8343 cnttype = g_strdup(text);
8346 alertpanel_error(_("Invalid MIME type."));
8352 ainfo->encoding = combobox_get_active_data(optmenu);
8354 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8355 if (*entry_text != '\0') {
8356 if (is_file_exist(entry_text) &&
8357 (size = get_file_size(entry_text)) > 0)
8358 file = g_strdup(entry_text);
8361 (_("File doesn't exist or is empty."));
8367 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8368 if (*entry_text != '\0') {
8369 g_free(ainfo->name);
8370 ainfo->name = g_strdup(entry_text);
8374 g_free(ainfo->content_type);
8375 ainfo->content_type = cnttype;
8378 g_free(ainfo->file);
8382 ainfo->size = (goffset)size;
8384 /* update tree store */
8385 text = to_human_readable(ainfo->size);
8386 gtk_tree_model_get_iter(model, &iter, path);
8387 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8388 COL_MIMETYPE, ainfo->content_type,
8390 COL_NAME, ainfo->name,
8396 gtk_tree_path_free(path);
8399 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8401 label = gtk_label_new(str); \
8402 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8403 GTK_FILL, 0, 0, 0); \
8404 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8406 entry = gtk_entry_new(); \
8407 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8408 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8411 static void compose_attach_property_create(gboolean *cancelled)
8417 GtkWidget *mimetype_entry;
8420 GtkListStore *optmenu_menu;
8421 GtkWidget *path_entry;
8422 GtkWidget *filename_entry;
8425 GtkWidget *cancel_btn;
8426 GList *mime_type_list, *strlist;
8429 debug_print("Creating attach_property window...\n");
8431 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8432 gtk_widget_set_size_request(window, 480, -1);
8433 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8434 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8435 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8436 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8437 g_signal_connect(G_OBJECT(window), "delete_event",
8438 G_CALLBACK(attach_property_delete_event),
8440 g_signal_connect(G_OBJECT(window), "key_press_event",
8441 G_CALLBACK(attach_property_key_pressed),
8444 vbox = gtk_vbox_new(FALSE, 8);
8445 gtk_container_add(GTK_CONTAINER(window), vbox);
8447 table = gtk_table_new(4, 2, FALSE);
8448 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8449 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8450 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8452 label = gtk_label_new(_("MIME type"));
8453 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8455 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8456 mimetype_entry = gtk_combo_box_entry_new_text();
8457 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8458 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8460 /* stuff with list */
8461 mime_type_list = procmime_get_mime_type_list();
8463 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8464 MimeType *type = (MimeType *) mime_type_list->data;
8467 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8469 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8472 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8473 (GCompareFunc)strcmp2);
8476 for (mime_type_list = strlist; mime_type_list != NULL;
8477 mime_type_list = mime_type_list->next) {
8478 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8479 g_free(mime_type_list->data);
8481 g_list_free(strlist);
8482 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8483 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8485 label = gtk_label_new(_("Encoding"));
8486 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8488 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8490 hbox = gtk_hbox_new(FALSE, 0);
8491 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8492 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8494 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8495 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8497 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8498 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8499 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8500 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8501 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8503 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8505 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8506 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8508 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8509 &ok_btn, GTK_STOCK_OK,
8511 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8512 gtk_widget_grab_default(ok_btn);
8514 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8515 G_CALLBACK(attach_property_ok),
8517 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8518 G_CALLBACK(attach_property_cancel),
8521 gtk_widget_show_all(vbox);
8523 attach_prop.window = window;
8524 attach_prop.mimetype_entry = mimetype_entry;
8525 attach_prop.encoding_optmenu = optmenu;
8526 attach_prop.path_entry = path_entry;
8527 attach_prop.filename_entry = filename_entry;
8528 attach_prop.ok_btn = ok_btn;
8529 attach_prop.cancel_btn = cancel_btn;
8532 #undef SET_LABEL_AND_ENTRY
8534 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8540 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8546 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8547 gboolean *cancelled)
8555 static gboolean attach_property_key_pressed(GtkWidget *widget,
8557 gboolean *cancelled)
8559 if (event && event->keyval == GDK_Escape) {
8563 if (event && event->keyval == GDK_Return) {
8571 static void compose_exec_ext_editor(Compose *compose)
8578 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8579 G_DIR_SEPARATOR, compose);
8581 if (pipe(pipe_fds) < 0) {
8587 if ((pid = fork()) < 0) {
8594 /* close the write side of the pipe */
8597 compose->exteditor_file = g_strdup(tmp);
8598 compose->exteditor_pid = pid;
8600 compose_set_ext_editor_sensitive(compose, FALSE);
8603 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8605 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8607 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8611 } else { /* process-monitoring process */
8617 /* close the read side of the pipe */
8620 if (compose_write_body_to_file(compose, tmp) < 0) {
8621 fd_write_all(pipe_fds[1], "2\n", 2);
8625 pid_ed = compose_exec_ext_editor_real(tmp);
8627 fd_write_all(pipe_fds[1], "1\n", 2);
8631 /* wait until editor is terminated */
8632 waitpid(pid_ed, NULL, 0);
8634 fd_write_all(pipe_fds[1], "0\n", 2);
8641 #endif /* G_OS_UNIX */
8645 static gint compose_exec_ext_editor_real(const gchar *file)
8652 cm_return_val_if_fail(file != NULL, -1);
8654 if ((pid = fork()) < 0) {
8659 if (pid != 0) return pid;
8661 /* grandchild process */
8663 if (setpgid(0, getppid()))
8666 if (prefs_common_get_ext_editor_cmd() &&
8667 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8668 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8669 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8671 if (prefs_common_get_ext_editor_cmd())
8672 g_warning("External editor command-line is invalid: '%s'\n",
8673 prefs_common_get_ext_editor_cmd());
8674 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8677 cmdline = strsplit_with_quote(buf, " ", 1024);
8678 execvp(cmdline[0], cmdline);
8681 g_strfreev(cmdline);
8686 static gboolean compose_ext_editor_kill(Compose *compose)
8688 pid_t pgid = compose->exteditor_pid * -1;
8691 ret = kill(pgid, 0);
8693 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8697 msg = g_strdup_printf
8698 (_("The external editor is still working.\n"
8699 "Force terminating the process?\n"
8700 "process group id: %d"), -pgid);
8701 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8702 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8706 if (val == G_ALERTALTERNATE) {
8707 g_source_remove(compose->exteditor_tag);
8708 g_io_channel_shutdown(compose->exteditor_ch,
8710 g_io_channel_unref(compose->exteditor_ch);
8712 if (kill(pgid, SIGTERM) < 0) perror("kill");
8713 waitpid(compose->exteditor_pid, NULL, 0);
8715 g_warning("Terminated process group id: %d", -pgid);
8716 g_warning("Temporary file: %s",
8717 compose->exteditor_file);
8719 compose_set_ext_editor_sensitive(compose, TRUE);
8721 g_free(compose->exteditor_file);
8722 compose->exteditor_file = NULL;
8723 compose->exteditor_pid = -1;
8724 compose->exteditor_ch = NULL;
8725 compose->exteditor_tag = -1;
8733 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8737 Compose *compose = (Compose *)data;
8740 debug_print(_("Compose: input from monitoring process\n"));
8742 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8744 g_io_channel_shutdown(source, FALSE, NULL);
8745 g_io_channel_unref(source);
8747 waitpid(compose->exteditor_pid, NULL, 0);
8749 if (buf[0] == '0') { /* success */
8750 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8751 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8753 gtk_text_buffer_set_text(buffer, "", -1);
8754 compose_insert_file(compose, compose->exteditor_file);
8755 compose_changed_cb(NULL, compose);
8756 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8758 if (claws_unlink(compose->exteditor_file) < 0)
8759 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8760 } else if (buf[0] == '1') { /* failed */
8761 g_warning("Couldn't exec external editor\n");
8762 if (claws_unlink(compose->exteditor_file) < 0)
8763 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8764 } else if (buf[0] == '2') {
8765 g_warning("Couldn't write to file\n");
8766 } else if (buf[0] == '3') {
8767 g_warning("Pipe read failed\n");
8770 compose_set_ext_editor_sensitive(compose, TRUE);
8772 g_free(compose->exteditor_file);
8773 compose->exteditor_file = NULL;
8774 compose->exteditor_pid = -1;
8775 compose->exteditor_ch = NULL;
8776 compose->exteditor_tag = -1;
8781 static void compose_set_ext_editor_sensitive(Compose *compose,
8784 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8785 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8786 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8787 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8788 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8789 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8790 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8792 gtk_widget_set_sensitive(compose->text, sensitive);
8793 if (compose->toolbar->send_btn)
8794 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8795 if (compose->toolbar->sendl_btn)
8796 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8797 if (compose->toolbar->draft_btn)
8798 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8799 if (compose->toolbar->insert_btn)
8800 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8801 if (compose->toolbar->sig_btn)
8802 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8803 if (compose->toolbar->exteditor_btn)
8804 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8805 if (compose->toolbar->linewrap_current_btn)
8806 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8807 if (compose->toolbar->linewrap_all_btn)
8808 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8810 #endif /* G_OS_UNIX */
8813 * compose_undo_state_changed:
8815 * Change the sensivity of the menuentries undo and redo
8817 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8818 gint redo_state, gpointer data)
8820 Compose *compose = (Compose *)data;
8822 switch (undo_state) {
8823 case UNDO_STATE_TRUE:
8824 if (!undostruct->undo_state) {
8825 undostruct->undo_state = TRUE;
8826 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8829 case UNDO_STATE_FALSE:
8830 if (undostruct->undo_state) {
8831 undostruct->undo_state = FALSE;
8832 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8835 case UNDO_STATE_UNCHANGED:
8837 case UNDO_STATE_REFRESH:
8838 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8841 g_warning("Undo state not recognized");
8845 switch (redo_state) {
8846 case UNDO_STATE_TRUE:
8847 if (!undostruct->redo_state) {
8848 undostruct->redo_state = TRUE;
8849 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8852 case UNDO_STATE_FALSE:
8853 if (undostruct->redo_state) {
8854 undostruct->redo_state = FALSE;
8855 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8858 case UNDO_STATE_UNCHANGED:
8860 case UNDO_STATE_REFRESH:
8861 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8864 g_warning("Redo state not recognized");
8869 /* callback functions */
8871 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8872 * includes "non-client" (windows-izm) in calculation, so this calculation
8873 * may not be accurate.
8875 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8876 GtkAllocation *allocation,
8877 GtkSHRuler *shruler)
8879 if (prefs_common.show_ruler) {
8880 gint char_width = 0, char_height = 0;
8881 gint line_width_in_chars;
8883 gtkut_get_font_size(GTK_WIDGET(widget),
8884 &char_width, &char_height);
8885 line_width_in_chars =
8886 (allocation->width - allocation->x) / char_width;
8888 /* got the maximum */
8889 gtk_ruler_set_range(GTK_RULER(shruler),
8890 0.0, line_width_in_chars, 0,
8891 /*line_width_in_chars*/ char_width);
8897 static void account_activated(GtkComboBox *optmenu, gpointer data)
8899 Compose *compose = (Compose *)data;
8902 gchar *folderidentifier;
8903 gint account_id = 0;
8907 /* Get ID of active account in the combo box */
8908 menu = gtk_combo_box_get_model(optmenu);
8909 gtk_combo_box_get_active_iter(optmenu, &iter);
8910 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8912 ac = account_find_from_id(account_id);
8913 cm_return_if_fail(ac != NULL);
8915 if (ac != compose->account)
8916 compose_select_account(compose, ac, FALSE);
8918 /* Set message save folder */
8919 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8920 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8922 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8923 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8925 compose_set_save_to(compose, NULL);
8926 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8927 folderidentifier = folder_item_get_identifier(account_get_special_folder
8928 (compose->account, F_OUTBOX));
8929 compose_set_save_to(compose, folderidentifier);
8930 g_free(folderidentifier);
8934 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8935 GtkTreeViewColumn *column, Compose *compose)
8937 compose_attach_property(NULL, compose);
8940 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8943 Compose *compose = (Compose *)data;
8944 GtkTreeSelection *attach_selection;
8945 gint attach_nr_selected;
8947 if (!event) return FALSE;
8949 if (event->button == 3) {
8950 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8951 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8953 if (attach_nr_selected > 0)
8955 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8956 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8958 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8959 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8962 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8963 NULL, NULL, event->button, event->time);
8970 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8973 Compose *compose = (Compose *)data;
8975 if (!event) return FALSE;
8977 switch (event->keyval) {
8979 compose_attach_remove_selected(NULL, compose);
8985 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8987 toolbar_comp_set_sensitive(compose, allow);
8988 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8989 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8991 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8993 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8994 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8995 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8997 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9001 static void compose_send_cb(GtkAction *action, gpointer data)
9003 Compose *compose = (Compose *)data;
9005 if (prefs_common.work_offline &&
9006 !inc_offline_should_override(TRUE,
9007 _("Claws Mail needs network access in order "
9008 "to send this email.")))
9011 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9012 g_source_remove(compose->draft_timeout_tag);
9013 compose->draft_timeout_tag = -1;
9016 compose_send(compose);
9019 static void compose_send_later_cb(GtkAction *action, gpointer data)
9021 Compose *compose = (Compose *)data;
9025 compose_allow_user_actions(compose, FALSE);
9026 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9027 compose_allow_user_actions(compose, TRUE);
9031 compose_close(compose);
9032 } else if (val == -1) {
9033 alertpanel_error(_("Could not queue message."));
9034 } else if (val == -2) {
9035 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9036 } else if (val == -3) {
9037 if (privacy_peek_error())
9038 alertpanel_error(_("Could not queue message for sending:\n\n"
9039 "Signature failed: %s"), privacy_get_error());
9040 } else if (val == -4) {
9041 alertpanel_error(_("Could not queue message for sending:\n\n"
9042 "Charset conversion failed."));
9043 } else if (val == -5) {
9044 alertpanel_error(_("Could not queue message for sending:\n\n"
9045 "Couldn't get recipient encryption key."));
9046 } else if (val == -6) {
9049 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9052 #define DRAFTED_AT_EXIT "drafted_at_exit"
9053 static void compose_register_draft(MsgInfo *info)
9055 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9056 DRAFTED_AT_EXIT, NULL);
9057 FILE *fp = g_fopen(filepath, "ab");
9060 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9068 gboolean compose_draft (gpointer data, guint action)
9070 Compose *compose = (Compose *)data;
9074 MsgFlags flag = {0, 0};
9075 static gboolean lock = FALSE;
9076 MsgInfo *newmsginfo;
9078 gboolean target_locked = FALSE;
9079 gboolean err = FALSE;
9081 if (lock) return FALSE;
9083 if (compose->sending)
9086 draft = account_get_special_folder(compose->account, F_DRAFT);
9087 cm_return_val_if_fail(draft != NULL, FALSE);
9089 if (!g_mutex_trylock(compose->mutex)) {
9090 /* we don't want to lock the mutex once it's available,
9091 * because as the only other part of compose.c locking
9092 * it is compose_close - which means once unlocked,
9093 * the compose struct will be freed */
9094 debug_print("couldn't lock mutex, probably sending\n");
9100 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9101 G_DIR_SEPARATOR, compose);
9102 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9103 FILE_OP_ERROR(tmp, "fopen");
9107 /* chmod for security */
9108 if (change_file_mode_rw(fp, tmp) < 0) {
9109 FILE_OP_ERROR(tmp, "chmod");
9110 g_warning("can't change file mode\n");
9113 /* Save draft infos */
9114 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9115 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9117 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9118 gchar *savefolderid;
9120 savefolderid = compose_get_save_to(compose);
9121 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9122 g_free(savefolderid);
9124 if (compose->return_receipt) {
9125 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9127 if (compose->privacy_system) {
9128 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9129 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9130 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9133 /* Message-ID of message replying to */
9134 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9137 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9138 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9141 /* Message-ID of message forwarding to */
9142 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9145 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9146 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9150 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9151 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9153 /* end of headers */
9154 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9161 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9165 if (fclose(fp) == EOF) {
9169 if (compose->targetinfo) {
9170 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9171 flag.perm_flags = target_locked?MSG_LOCKED:0;
9173 flag.tmp_flags = MSG_DRAFT;
9175 folder_item_scan(draft);
9176 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9177 MsgInfo *tmpinfo = NULL;
9178 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9179 if (compose->msgid) {
9180 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9183 msgnum = tmpinfo->msgnum;
9184 procmsg_msginfo_free(tmpinfo);
9185 debug_print("got draft msgnum %d from scanning\n", msgnum);
9187 debug_print("didn't get draft msgnum after scanning\n");
9190 debug_print("got draft msgnum %d from adding\n", msgnum);
9196 if (action != COMPOSE_AUTO_SAVE) {
9197 if (action != COMPOSE_DRAFT_FOR_EXIT)
9198 alertpanel_error(_("Could not save draft."));
9201 gtkut_window_popup(compose->window);
9202 val = alertpanel_full(_("Could not save draft"),
9203 _("Could not save draft.\n"
9204 "Do you want to cancel exit or discard this email?"),
9205 _("_Cancel exit"), _("_Discard email"), NULL,
9206 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9207 if (val == G_ALERTALTERNATE) {
9209 g_mutex_unlock(compose->mutex); /* must be done before closing */
9210 compose_close(compose);
9214 g_mutex_unlock(compose->mutex); /* must be done before closing */
9223 if (compose->mode == COMPOSE_REEDIT) {
9224 compose_remove_reedit_target(compose, TRUE);
9227 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9230 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9232 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9234 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9235 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9236 procmsg_msginfo_set_flags(newmsginfo, 0,
9237 MSG_HAS_ATTACHMENT);
9239 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9240 compose_register_draft(newmsginfo);
9242 procmsg_msginfo_free(newmsginfo);
9245 folder_item_scan(draft);
9247 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9249 g_mutex_unlock(compose->mutex); /* must be done before closing */
9250 compose_close(compose);
9256 path = folder_item_fetch_msg(draft, msgnum);
9258 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9261 if (g_stat(path, &s) < 0) {
9262 FILE_OP_ERROR(path, "stat");
9268 procmsg_msginfo_free(compose->targetinfo);
9269 compose->targetinfo = procmsg_msginfo_new();
9270 compose->targetinfo->msgnum = msgnum;
9271 compose->targetinfo->size = (goffset)s.st_size;
9272 compose->targetinfo->mtime = s.st_mtime;
9273 compose->targetinfo->folder = draft;
9275 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9276 compose->mode = COMPOSE_REEDIT;
9278 if (action == COMPOSE_AUTO_SAVE) {
9279 compose->autosaved_draft = compose->targetinfo;
9281 compose->modified = FALSE;
9282 compose_set_title(compose);
9286 g_mutex_unlock(compose->mutex);
9290 void compose_clear_exit_drafts(void)
9292 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9293 DRAFTED_AT_EXIT, NULL);
9294 if (is_file_exist(filepath))
9295 claws_unlink(filepath);
9300 void compose_reopen_exit_drafts(void)
9302 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9303 DRAFTED_AT_EXIT, NULL);
9304 FILE *fp = g_fopen(filepath, "rb");
9308 while (fgets(buf, sizeof(buf), fp)) {
9309 gchar **parts = g_strsplit(buf, "\t", 2);
9310 const gchar *folder = parts[0];
9311 int msgnum = parts[1] ? atoi(parts[1]):-1;
9313 if (folder && *folder && msgnum > -1) {
9314 FolderItem *item = folder_find_item_from_identifier(folder);
9315 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9317 compose_reedit(info, FALSE);
9324 compose_clear_exit_drafts();
9327 static void compose_save_cb(GtkAction *action, gpointer data)
9329 Compose *compose = (Compose *)data;
9330 compose_draft(compose, COMPOSE_KEEP_EDITING);
9331 compose->rmode = COMPOSE_REEDIT;
9334 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9336 if (compose && file_list) {
9339 for ( tmp = file_list; tmp; tmp = tmp->next) {
9340 gchar *file = (gchar *) tmp->data;
9341 gchar *utf8_filename = conv_filename_to_utf8(file);
9342 compose_attach_append(compose, file, utf8_filename, NULL);
9343 compose_changed_cb(NULL, compose);
9348 g_free(utf8_filename);
9353 static void compose_attach_cb(GtkAction *action, gpointer data)
9355 Compose *compose = (Compose *)data;
9358 if (compose->redirect_filename != NULL)
9361 file_list = filesel_select_multiple_files_open(_("Select file"));
9364 compose_attach_from_list(compose, file_list, TRUE);
9365 g_list_free(file_list);
9369 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9371 Compose *compose = (Compose *)data;
9374 file_list = filesel_select_multiple_files_open(_("Select file"));
9379 for ( tmp = file_list; tmp; tmp = tmp->next) {
9380 gchar *file = (gchar *) tmp->data;
9381 gchar *filedup = g_strdup(file);
9382 gchar *shortfile = g_path_get_basename(filedup);
9383 ComposeInsertResult res;
9385 res = compose_insert_file(compose, file);
9386 if (res == COMPOSE_INSERT_READ_ERROR) {
9387 alertpanel_error(_("File '%s' could not be read."), shortfile);
9388 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9389 alertpanel_error(_("File '%s' contained invalid characters\n"
9390 "for the current encoding, insertion may be incorrect."), shortfile);
9396 g_list_free(file_list);
9400 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9402 Compose *compose = (Compose *)data;
9404 compose_insert_sig(compose, FALSE);
9407 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9411 Compose *compose = (Compose *)data;
9413 gtkut_widget_get_uposition(widget, &x, &y);
9414 if (!compose->batch) {
9415 prefs_common.compose_x = x;
9416 prefs_common.compose_y = y;
9418 if (compose->sending || compose->updating)
9420 compose_close_cb(NULL, compose);
9424 void compose_close_toolbar(Compose *compose)
9426 compose_close_cb(NULL, compose);
9429 static void compose_close_cb(GtkAction *action, gpointer data)
9431 Compose *compose = (Compose *)data;
9435 if (compose->exteditor_tag != -1) {
9436 if (!compose_ext_editor_kill(compose))
9441 if (compose->modified) {
9442 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9443 if (!g_mutex_trylock(compose->mutex)) {
9444 /* we don't want to lock the mutex once it's available,
9445 * because as the only other part of compose.c locking
9446 * it is compose_close - which means once unlocked,
9447 * the compose struct will be freed */
9448 debug_print("couldn't lock mutex, probably sending\n");
9452 val = alertpanel(_("Discard message"),
9453 _("This message has been modified. Discard it?"),
9454 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9456 val = alertpanel(_("Save changes"),
9457 _("This message has been modified. Save the latest changes?"),
9458 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9460 g_mutex_unlock(compose->mutex);
9462 case G_ALERTDEFAULT:
9463 if (prefs_common.autosave && !reedit)
9464 compose_remove_draft(compose);
9466 case G_ALERTALTERNATE:
9467 compose_draft(data, COMPOSE_QUIT_EDITING);
9474 compose_close(compose);
9477 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9479 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9480 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9481 Compose *compose = (Compose *) data;
9484 compose->out_encoding = (CharSet)value;
9487 static void compose_address_cb(GtkAction *action, gpointer data)
9489 Compose *compose = (Compose *)data;
9491 addressbook_open(compose);
9494 static void about_show_cb(GtkAction *action, gpointer data)
9499 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9501 Compose *compose = (Compose *)data;
9506 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9507 cm_return_if_fail(tmpl != NULL);
9509 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9511 val = alertpanel(_("Apply template"), msg,
9512 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9515 if (val == G_ALERTDEFAULT)
9516 compose_template_apply(compose, tmpl, TRUE);
9517 else if (val == G_ALERTALTERNATE)
9518 compose_template_apply(compose, tmpl, FALSE);
9521 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9523 Compose *compose = (Compose *)data;
9525 compose_exec_ext_editor(compose);
9528 static void compose_undo_cb(GtkAction *action, gpointer data)
9530 Compose *compose = (Compose *)data;
9531 gboolean prev_autowrap = compose->autowrap;
9533 compose->autowrap = FALSE;
9534 undo_undo(compose->undostruct);
9535 compose->autowrap = prev_autowrap;
9538 static void compose_redo_cb(GtkAction *action, gpointer data)
9540 Compose *compose = (Compose *)data;
9541 gboolean prev_autowrap = compose->autowrap;
9543 compose->autowrap = FALSE;
9544 undo_redo(compose->undostruct);
9545 compose->autowrap = prev_autowrap;
9548 static void entry_cut_clipboard(GtkWidget *entry)
9550 if (GTK_IS_EDITABLE(entry))
9551 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9552 else if (GTK_IS_TEXT_VIEW(entry))
9553 gtk_text_buffer_cut_clipboard(
9554 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9555 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9559 static void entry_copy_clipboard(GtkWidget *entry)
9561 if (GTK_IS_EDITABLE(entry))
9562 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9563 else if (GTK_IS_TEXT_VIEW(entry))
9564 gtk_text_buffer_copy_clipboard(
9565 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9566 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9569 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9570 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9572 if (GTK_IS_TEXT_VIEW(entry)) {
9573 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9574 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9575 GtkTextIter start_iter, end_iter;
9577 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9579 if (contents == NULL)
9582 /* we shouldn't delete the selection when middle-click-pasting, or we
9583 * can't mid-click-paste our own selection */
9584 if (clip != GDK_SELECTION_PRIMARY) {
9585 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9586 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9589 if (insert_place == NULL) {
9590 /* if insert_place isn't specified, insert at the cursor.
9591 * used for Ctrl-V pasting */
9592 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9593 start = gtk_text_iter_get_offset(&start_iter);
9594 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9596 /* if insert_place is specified, paste here.
9597 * used for mid-click-pasting */
9598 start = gtk_text_iter_get_offset(insert_place);
9599 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9600 if (prefs_common.primary_paste_unselects)
9601 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9605 /* paste unwrapped: mark the paste so it's not wrapped later */
9606 end = start + strlen(contents);
9607 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9608 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9609 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9610 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9611 /* rewrap paragraph now (after a mid-click-paste) */
9612 mark_start = gtk_text_buffer_get_insert(buffer);
9613 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9614 gtk_text_iter_backward_char(&start_iter);
9615 compose_beautify_paragraph(compose, &start_iter, TRUE);
9617 } else if (GTK_IS_EDITABLE(entry))
9618 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9620 compose->modified = TRUE;
9623 static void entry_allsel(GtkWidget *entry)
9625 if (GTK_IS_EDITABLE(entry))
9626 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9627 else if (GTK_IS_TEXT_VIEW(entry)) {
9628 GtkTextIter startiter, enditer;
9629 GtkTextBuffer *textbuf;
9631 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9632 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9633 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9635 gtk_text_buffer_move_mark_by_name(textbuf,
9636 "selection_bound", &startiter);
9637 gtk_text_buffer_move_mark_by_name(textbuf,
9638 "insert", &enditer);
9642 static void compose_cut_cb(GtkAction *action, gpointer data)
9644 Compose *compose = (Compose *)data;
9645 if (compose->focused_editable
9646 #ifndef GENERIC_UMPC
9647 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9650 entry_cut_clipboard(compose->focused_editable);
9653 static void compose_copy_cb(GtkAction *action, gpointer data)
9655 Compose *compose = (Compose *)data;
9656 if (compose->focused_editable
9657 #ifndef GENERIC_UMPC
9658 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9661 entry_copy_clipboard(compose->focused_editable);
9664 static void compose_paste_cb(GtkAction *action, gpointer data)
9666 Compose *compose = (Compose *)data;
9668 GtkTextBuffer *buffer;
9670 if (compose->focused_editable &&
9671 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9672 entry_paste_clipboard(compose, compose->focused_editable,
9673 prefs_common.linewrap_pastes,
9674 GDK_SELECTION_CLIPBOARD, NULL);
9678 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9680 Compose *compose = (Compose *)data;
9681 gint wrap_quote = prefs_common.linewrap_quote;
9682 if (compose->focused_editable
9683 #ifndef GENERIC_UMPC
9684 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9687 /* let text_insert() (called directly or at a later time
9688 * after the gtk_editable_paste_clipboard) know that
9689 * text is to be inserted as a quotation. implemented
9690 * by using a simple refcount... */
9691 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9692 G_OBJECT(compose->focused_editable),
9693 "paste_as_quotation"));
9694 g_object_set_data(G_OBJECT(compose->focused_editable),
9695 "paste_as_quotation",
9696 GINT_TO_POINTER(paste_as_quotation + 1));
9697 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9698 entry_paste_clipboard(compose, compose->focused_editable,
9699 prefs_common.linewrap_pastes,
9700 GDK_SELECTION_CLIPBOARD, NULL);
9701 prefs_common.linewrap_quote = wrap_quote;
9705 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9707 Compose *compose = (Compose *)data;
9709 GtkTextBuffer *buffer;
9711 if (compose->focused_editable
9712 #ifndef GENERIC_UMPC
9713 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9716 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9717 GDK_SELECTION_CLIPBOARD, NULL);
9721 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9723 Compose *compose = (Compose *)data;
9725 GtkTextBuffer *buffer;
9727 if (compose->focused_editable
9728 #ifndef GENERIC_UMPC
9729 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9732 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9733 GDK_SELECTION_CLIPBOARD, NULL);
9737 static void compose_allsel_cb(GtkAction *action, gpointer data)
9739 Compose *compose = (Compose *)data;
9740 if (compose->focused_editable
9741 #ifndef GENERIC_UMPC
9742 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9745 entry_allsel(compose->focused_editable);
9748 static void textview_move_beginning_of_line (GtkTextView *text)
9750 GtkTextBuffer *buffer;
9754 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9756 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9757 mark = gtk_text_buffer_get_insert(buffer);
9758 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9759 gtk_text_iter_set_line_offset(&ins, 0);
9760 gtk_text_buffer_place_cursor(buffer, &ins);
9763 static void textview_move_forward_character (GtkTextView *text)
9765 GtkTextBuffer *buffer;
9769 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9771 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9772 mark = gtk_text_buffer_get_insert(buffer);
9773 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9774 if (gtk_text_iter_forward_cursor_position(&ins))
9775 gtk_text_buffer_place_cursor(buffer, &ins);
9778 static void textview_move_backward_character (GtkTextView *text)
9780 GtkTextBuffer *buffer;
9784 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9786 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9787 mark = gtk_text_buffer_get_insert(buffer);
9788 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9789 if (gtk_text_iter_backward_cursor_position(&ins))
9790 gtk_text_buffer_place_cursor(buffer, &ins);
9793 static void textview_move_forward_word (GtkTextView *text)
9795 GtkTextBuffer *buffer;
9800 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9802 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9803 mark = gtk_text_buffer_get_insert(buffer);
9804 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9805 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9806 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9807 gtk_text_iter_backward_word_start(&ins);
9808 gtk_text_buffer_place_cursor(buffer, &ins);
9812 static void textview_move_backward_word (GtkTextView *text)
9814 GtkTextBuffer *buffer;
9819 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9821 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9822 mark = gtk_text_buffer_get_insert(buffer);
9823 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9824 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9825 if (gtk_text_iter_backward_word_starts(&ins, 1))
9826 gtk_text_buffer_place_cursor(buffer, &ins);
9829 static void textview_move_end_of_line (GtkTextView *text)
9831 GtkTextBuffer *buffer;
9835 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9837 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9838 mark = gtk_text_buffer_get_insert(buffer);
9839 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9840 if (gtk_text_iter_forward_to_line_end(&ins))
9841 gtk_text_buffer_place_cursor(buffer, &ins);
9844 static void textview_move_next_line (GtkTextView *text)
9846 GtkTextBuffer *buffer;
9851 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9853 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9854 mark = gtk_text_buffer_get_insert(buffer);
9855 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9856 offset = gtk_text_iter_get_line_offset(&ins);
9857 if (gtk_text_iter_forward_line(&ins)) {
9858 gtk_text_iter_set_line_offset(&ins, offset);
9859 gtk_text_buffer_place_cursor(buffer, &ins);
9863 static void textview_move_previous_line (GtkTextView *text)
9865 GtkTextBuffer *buffer;
9870 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9872 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9873 mark = gtk_text_buffer_get_insert(buffer);
9874 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9875 offset = gtk_text_iter_get_line_offset(&ins);
9876 if (gtk_text_iter_backward_line(&ins)) {
9877 gtk_text_iter_set_line_offset(&ins, offset);
9878 gtk_text_buffer_place_cursor(buffer, &ins);
9882 static void textview_delete_forward_character (GtkTextView *text)
9884 GtkTextBuffer *buffer;
9886 GtkTextIter ins, end_iter;
9888 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9890 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9891 mark = gtk_text_buffer_get_insert(buffer);
9892 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9894 if (gtk_text_iter_forward_char(&end_iter)) {
9895 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9899 static void textview_delete_backward_character (GtkTextView *text)
9901 GtkTextBuffer *buffer;
9903 GtkTextIter ins, end_iter;
9905 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9907 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9908 mark = gtk_text_buffer_get_insert(buffer);
9909 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9911 if (gtk_text_iter_backward_char(&end_iter)) {
9912 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9916 static void textview_delete_forward_word (GtkTextView *text)
9918 GtkTextBuffer *buffer;
9920 GtkTextIter ins, end_iter;
9922 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9924 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9925 mark = gtk_text_buffer_get_insert(buffer);
9926 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9928 if (gtk_text_iter_forward_word_end(&end_iter)) {
9929 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9933 static void textview_delete_backward_word (GtkTextView *text)
9935 GtkTextBuffer *buffer;
9937 GtkTextIter ins, end_iter;
9939 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9941 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9942 mark = gtk_text_buffer_get_insert(buffer);
9943 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9945 if (gtk_text_iter_backward_word_start(&end_iter)) {
9946 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9950 static void textview_delete_line (GtkTextView *text)
9952 GtkTextBuffer *buffer;
9954 GtkTextIter ins, start_iter, end_iter;
9956 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9958 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9959 mark = gtk_text_buffer_get_insert(buffer);
9960 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9963 gtk_text_iter_set_line_offset(&start_iter, 0);
9966 if (gtk_text_iter_ends_line(&end_iter)){
9967 if (!gtk_text_iter_forward_char(&end_iter))
9968 gtk_text_iter_backward_char(&start_iter);
9971 gtk_text_iter_forward_to_line_end(&end_iter);
9972 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9975 static void textview_delete_to_line_end (GtkTextView *text)
9977 GtkTextBuffer *buffer;
9979 GtkTextIter ins, end_iter;
9981 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9983 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9984 mark = gtk_text_buffer_get_insert(buffer);
9985 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9987 if (gtk_text_iter_ends_line(&end_iter))
9988 gtk_text_iter_forward_char(&end_iter);
9990 gtk_text_iter_forward_to_line_end(&end_iter);
9991 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9994 #define DO_ACTION(name, act) { \
9995 if(!strcmp(name, a_name)) { \
9999 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10001 const gchar *a_name = gtk_action_get_name(action);
10002 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10003 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10004 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10005 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10006 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10007 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10008 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10009 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10010 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10011 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10012 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10013 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10014 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10015 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10019 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10021 Compose *compose = (Compose *)data;
10022 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10023 ComposeCallAdvancedAction action = -1;
10025 action = compose_call_advanced_action_from_path(gaction);
10028 void (*do_action) (GtkTextView *text);
10029 } action_table[] = {
10030 {textview_move_beginning_of_line},
10031 {textview_move_forward_character},
10032 {textview_move_backward_character},
10033 {textview_move_forward_word},
10034 {textview_move_backward_word},
10035 {textview_move_end_of_line},
10036 {textview_move_next_line},
10037 {textview_move_previous_line},
10038 {textview_delete_forward_character},
10039 {textview_delete_backward_character},
10040 {textview_delete_forward_word},
10041 {textview_delete_backward_word},
10042 {textview_delete_line},
10043 {textview_delete_to_line_end}
10046 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10048 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10049 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10050 if (action_table[action].do_action)
10051 action_table[action].do_action(text);
10053 g_warning("Not implemented yet.");
10057 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10061 if (GTK_IS_EDITABLE(widget)) {
10062 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10063 gtk_editable_set_position(GTK_EDITABLE(widget),
10066 if (widget->parent && widget->parent->parent
10067 && widget->parent->parent->parent) {
10068 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10069 gint y = widget->allocation.y;
10070 gint height = widget->allocation.height;
10071 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10072 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10074 if (y < (int)shown->value) {
10075 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10077 if (y + height > (int)shown->value + (int)shown->page_size) {
10078 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10079 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10080 y + height - (int)shown->page_size - 1);
10082 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10083 (int)shown->upper - (int)shown->page_size - 1);
10090 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10091 compose->focused_editable = widget;
10093 #ifdef GENERIC_UMPC
10094 if (GTK_IS_TEXT_VIEW(widget)
10095 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10096 g_object_ref(compose->notebook);
10097 g_object_ref(compose->edit_vbox);
10098 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10099 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10100 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10101 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10102 g_object_unref(compose->notebook);
10103 g_object_unref(compose->edit_vbox);
10104 g_signal_handlers_block_by_func(G_OBJECT(widget),
10105 G_CALLBACK(compose_grab_focus_cb),
10107 gtk_widget_grab_focus(widget);
10108 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10109 G_CALLBACK(compose_grab_focus_cb),
10111 } else if (!GTK_IS_TEXT_VIEW(widget)
10112 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10113 g_object_ref(compose->notebook);
10114 g_object_ref(compose->edit_vbox);
10115 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10116 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10117 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10118 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10119 g_object_unref(compose->notebook);
10120 g_object_unref(compose->edit_vbox);
10121 g_signal_handlers_block_by_func(G_OBJECT(widget),
10122 G_CALLBACK(compose_grab_focus_cb),
10124 gtk_widget_grab_focus(widget);
10125 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10126 G_CALLBACK(compose_grab_focus_cb),
10132 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10134 compose->modified = TRUE;
10135 // compose_beautify_paragraph(compose, NULL, TRUE);
10136 #ifndef GENERIC_UMPC
10137 compose_set_title(compose);
10141 static void compose_wrap_cb(GtkAction *action, gpointer data)
10143 Compose *compose = (Compose *)data;
10144 compose_beautify_paragraph(compose, NULL, TRUE);
10147 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10149 Compose *compose = (Compose *)data;
10150 compose_wrap_all_full(compose, TRUE);
10153 static void compose_find_cb(GtkAction *action, gpointer data)
10155 Compose *compose = (Compose *)data;
10157 message_search_compose(compose);
10160 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10163 Compose *compose = (Compose *)data;
10164 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10165 if (compose->autowrap)
10166 compose_wrap_all_full(compose, TRUE);
10167 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10170 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10173 Compose *compose = (Compose *)data;
10174 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10177 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10179 Compose *compose = (Compose *)data;
10181 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10184 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10186 Compose *compose = (Compose *)data;
10188 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10191 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10193 g_free(compose->privacy_system);
10195 compose->privacy_system = g_strdup(account->default_privacy_system);
10196 compose_update_privacy_system_menu_item(compose, warn);
10199 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10201 Compose *compose = (Compose *)data;
10203 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10204 gtk_widget_show(compose->ruler_hbox);
10205 prefs_common.show_ruler = TRUE;
10207 gtk_widget_hide(compose->ruler_hbox);
10208 gtk_widget_queue_resize(compose->edit_vbox);
10209 prefs_common.show_ruler = FALSE;
10213 static void compose_attach_drag_received_cb (GtkWidget *widget,
10214 GdkDragContext *context,
10217 GtkSelectionData *data,
10220 gpointer user_data)
10222 Compose *compose = (Compose *)user_data;
10225 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10227 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10229 ) && gtk_drag_get_source_widget(context) !=
10230 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10231 list = uri_list_extract_filenames((const gchar *)data->data);
10232 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10233 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10234 compose_attach_append
10235 (compose, (const gchar *)tmp->data,
10236 utf8_filename, NULL);
10237 g_free(utf8_filename);
10239 if (list) compose_changed_cb(NULL, compose);
10240 list_free_strings(list);
10242 } else if (gtk_drag_get_source_widget(context)
10243 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10244 /* comes from our summaryview */
10245 SummaryView * summaryview = NULL;
10246 GSList * list = NULL, *cur = NULL;
10248 if (mainwindow_get_mainwindow())
10249 summaryview = mainwindow_get_mainwindow()->summaryview;
10252 list = summary_get_selected_msg_list(summaryview);
10254 for (cur = list; cur; cur = cur->next) {
10255 MsgInfo *msginfo = (MsgInfo *)cur->data;
10256 gchar *file = NULL;
10258 file = procmsg_get_message_file_full(msginfo,
10261 compose_attach_append(compose, (const gchar *)file,
10262 (const gchar *)file, "message/rfc822");
10266 g_slist_free(list);
10270 static gboolean compose_drag_drop(GtkWidget *widget,
10271 GdkDragContext *drag_context,
10273 guint time, gpointer user_data)
10275 /* not handling this signal makes compose_insert_drag_received_cb
10280 static void compose_insert_drag_received_cb (GtkWidget *widget,
10281 GdkDragContext *drag_context,
10284 GtkSelectionData *data,
10287 gpointer user_data)
10289 Compose *compose = (Compose *)user_data;
10292 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10295 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10297 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10299 AlertValue val = G_ALERTDEFAULT;
10301 list = uri_list_extract_filenames((const gchar *)data->data);
10302 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10303 /* Assume a list of no files, and data has ://, is a remote link */
10304 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10305 gchar *tmpfile = get_tmp_file();
10306 str_write_to_file(tmpdata, tmpfile);
10308 compose_insert_file(compose, tmpfile);
10309 claws_unlink(tmpfile);
10311 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10312 compose_beautify_paragraph(compose, NULL, TRUE);
10315 switch (prefs_common.compose_dnd_mode) {
10316 case COMPOSE_DND_ASK:
10317 val = alertpanel_full(_("Insert or attach?"),
10318 _("Do you want to insert the contents of the file(s) "
10319 "into the message body, or attach it to the email?"),
10320 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10321 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10323 case COMPOSE_DND_INSERT:
10324 val = G_ALERTALTERNATE;
10326 case COMPOSE_DND_ATTACH:
10327 val = G_ALERTOTHER;
10330 /* unexpected case */
10331 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10334 if (val & G_ALERTDISABLE) {
10335 val &= ~G_ALERTDISABLE;
10336 /* remember what action to perform by default, only if we don't click Cancel */
10337 if (val == G_ALERTALTERNATE)
10338 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10339 else if (val == G_ALERTOTHER)
10340 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10343 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10344 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10345 list_free_strings(list);
10348 } else if (val == G_ALERTOTHER) {
10349 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10350 list_free_strings(list);
10355 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10356 compose_insert_file(compose, (const gchar *)tmp->data);
10358 list_free_strings(list);
10360 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10363 #if GTK_CHECK_VERSION(2, 8, 0)
10364 /* do nothing, handled by GTK */
10366 gchar *tmpfile = get_tmp_file();
10367 str_write_to_file((const gchar *)data->data, tmpfile);
10368 compose_insert_file(compose, tmpfile);
10369 claws_unlink(tmpfile);
10371 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10375 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10378 static void compose_header_drag_received_cb (GtkWidget *widget,
10379 GdkDragContext *drag_context,
10382 GtkSelectionData *data,
10385 gpointer user_data)
10387 GtkEditable *entry = (GtkEditable *)user_data;
10388 gchar *email = (gchar *)data->data;
10390 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10393 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10394 gchar *decoded=g_new(gchar, strlen(email));
10397 email += strlen("mailto:");
10398 decode_uri(decoded, email); /* will fit */
10399 gtk_editable_delete_text(entry, 0, -1);
10400 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10401 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10405 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10408 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10410 Compose *compose = (Compose *)data;
10412 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10413 compose->return_receipt = TRUE;
10415 compose->return_receipt = FALSE;
10418 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10420 Compose *compose = (Compose *)data;
10422 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10423 compose->remove_references = TRUE;
10425 compose->remove_references = FALSE;
10428 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10429 ComposeHeaderEntry *headerentry)
10431 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10435 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10436 GdkEventKey *event,
10437 ComposeHeaderEntry *headerentry)
10439 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10440 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10441 !(event->state & GDK_MODIFIER_MASK) &&
10442 (event->keyval == GDK_BackSpace) &&
10443 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10444 gtk_container_remove
10445 (GTK_CONTAINER(headerentry->compose->header_table),
10446 headerentry->combo);
10447 gtk_container_remove
10448 (GTK_CONTAINER(headerentry->compose->header_table),
10449 headerentry->entry);
10450 headerentry->compose->header_list =
10451 g_slist_remove(headerentry->compose->header_list,
10453 g_free(headerentry);
10454 } else if (event->keyval == GDK_Tab) {
10455 if (headerentry->compose->header_last == headerentry) {
10456 /* Override default next focus, and give it to subject_entry
10457 * instead of notebook tabs
10459 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10460 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10467 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10468 ComposeHeaderEntry *headerentry)
10470 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10471 compose_create_header_entry(headerentry->compose);
10472 g_signal_handlers_disconnect_matched
10473 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10474 0, 0, NULL, NULL, headerentry);
10476 /* Automatically scroll down */
10477 compose_show_first_last_header(headerentry->compose, FALSE);
10483 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10485 GtkAdjustment *vadj;
10487 cm_return_if_fail(compose);
10488 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10489 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10491 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10492 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10493 gtk_adjustment_changed(vadj);
10496 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10497 const gchar *text, gint len, Compose *compose)
10499 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10500 (G_OBJECT(compose->text), "paste_as_quotation"));
10503 cm_return_if_fail(text != NULL);
10505 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10506 G_CALLBACK(text_inserted),
10508 if (paste_as_quotation) {
10510 const gchar *qmark;
10512 GtkTextIter start_iter;
10515 len = strlen(text);
10517 new_text = g_strndup(text, len);
10519 qmark = compose_quote_char_from_context(compose);
10521 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10522 gtk_text_buffer_place_cursor(buffer, iter);
10524 pos = gtk_text_iter_get_offset(iter);
10526 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10527 _("Quote format error at line %d."));
10528 quote_fmt_reset_vartable();
10530 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10531 GINT_TO_POINTER(paste_as_quotation - 1));
10533 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10534 gtk_text_buffer_place_cursor(buffer, iter);
10535 gtk_text_buffer_delete_mark(buffer, mark);
10537 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10538 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10539 compose_beautify_paragraph(compose, &start_iter, FALSE);
10540 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10541 gtk_text_buffer_delete_mark(buffer, mark);
10543 if (strcmp(text, "\n") || compose->automatic_break
10544 || gtk_text_iter_starts_line(iter)) {
10545 GtkTextIter before_ins;
10546 gtk_text_buffer_insert(buffer, iter, text, len);
10547 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10548 before_ins = *iter;
10549 gtk_text_iter_backward_chars(&before_ins, len);
10550 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10553 /* check if the preceding is just whitespace or quote */
10554 GtkTextIter start_line;
10555 gchar *tmp = NULL, *quote = NULL;
10556 gint quote_len = 0, is_normal = 0;
10557 start_line = *iter;
10558 gtk_text_iter_set_line_offset(&start_line, 0);
10559 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10562 if (*tmp == '\0') {
10565 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10573 gtk_text_buffer_insert(buffer, iter, text, len);
10575 gtk_text_buffer_insert_with_tags_by_name(buffer,
10576 iter, text, len, "no_join", NULL);
10581 if (!paste_as_quotation) {
10582 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10583 compose_beautify_paragraph(compose, iter, FALSE);
10584 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10585 gtk_text_buffer_delete_mark(buffer, mark);
10588 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10589 G_CALLBACK(text_inserted),
10591 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10593 if (prefs_common.autosave &&
10594 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10595 compose->draft_timeout_tag != -2 /* disabled while loading */)
10596 compose->draft_timeout_tag = g_timeout_add
10597 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10599 static gint compose_defer_auto_save_draft(Compose *compose)
10601 compose->draft_timeout_tag = -1;
10602 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10607 static void compose_check_all(GtkAction *action, gpointer data)
10609 Compose *compose = (Compose *)data;
10610 if (!compose->gtkaspell)
10613 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10614 claws_spell_entry_check_all(
10615 CLAWS_SPELL_ENTRY(compose->subject_entry));
10617 gtkaspell_check_all(compose->gtkaspell);
10620 static void compose_highlight_all(GtkAction *action, gpointer data)
10622 Compose *compose = (Compose *)data;
10623 if (compose->gtkaspell) {
10624 claws_spell_entry_recheck_all(
10625 CLAWS_SPELL_ENTRY(compose->subject_entry));
10626 gtkaspell_highlight_all(compose->gtkaspell);
10630 static void compose_check_backwards(GtkAction *action, gpointer data)
10632 Compose *compose = (Compose *)data;
10633 if (!compose->gtkaspell) {
10634 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10638 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10639 claws_spell_entry_check_backwards(
10640 CLAWS_SPELL_ENTRY(compose->subject_entry));
10642 gtkaspell_check_backwards(compose->gtkaspell);
10645 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10647 Compose *compose = (Compose *)data;
10648 if (!compose->gtkaspell) {
10649 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10653 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10654 claws_spell_entry_check_forwards_go(
10655 CLAWS_SPELL_ENTRY(compose->subject_entry));
10657 gtkaspell_check_forwards_go(compose->gtkaspell);
10662 *\brief Guess originating forward account from MsgInfo and several
10663 * "common preference" settings. Return NULL if no guess.
10665 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10667 PrefsAccount *account = NULL;
10669 cm_return_val_if_fail(msginfo, NULL);
10670 cm_return_val_if_fail(msginfo->folder, NULL);
10671 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10673 if (msginfo->folder->prefs->enable_default_account)
10674 account = account_find_from_id(msginfo->folder->prefs->default_account);
10677 account = msginfo->folder->folder->account;
10679 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10681 Xstrdup_a(to, msginfo->to, return NULL);
10682 extract_address(to);
10683 account = account_find_from_address(to, FALSE);
10686 if (!account && prefs_common.forward_account_autosel) {
10687 gchar cc[BUFFSIZE];
10688 if (!procheader_get_header_from_msginfo
10689 (msginfo, cc,sizeof cc , "Cc:")) {
10690 gchar *buf = cc + strlen("Cc:");
10691 extract_address(buf);
10692 account = account_find_from_address(buf, FALSE);
10696 if (!account && prefs_common.forward_account_autosel) {
10697 gchar deliveredto[BUFFSIZE];
10698 if (!procheader_get_header_from_msginfo
10699 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10700 gchar *buf = deliveredto + strlen("Delivered-To:");
10701 extract_address(buf);
10702 account = account_find_from_address(buf, FALSE);
10709 gboolean compose_close(Compose *compose)
10713 if (!g_mutex_trylock(compose->mutex)) {
10714 /* we have to wait for the (possibly deferred by auto-save)
10715 * drafting to be done, before destroying the compose under
10717 debug_print("waiting for drafting to finish...\n");
10718 compose_allow_user_actions(compose, FALSE);
10719 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10722 cm_return_val_if_fail(compose, FALSE);
10723 gtkut_widget_get_uposition(compose->window, &x, &y);
10724 if (!compose->batch) {
10725 prefs_common.compose_x = x;
10726 prefs_common.compose_y = y;
10728 g_mutex_unlock(compose->mutex);
10729 compose_destroy(compose);
10734 * Add entry field for each address in list.
10735 * \param compose E-Mail composition object.
10736 * \param listAddress List of (formatted) E-Mail addresses.
10738 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10741 node = listAddress;
10743 addr = ( gchar * ) node->data;
10744 compose_entry_append( compose, addr, COMPOSE_TO );
10745 node = g_list_next( node );
10749 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10750 guint action, gboolean opening_multiple)
10752 gchar *body = NULL;
10753 GSList *new_msglist = NULL;
10754 MsgInfo *tmp_msginfo = NULL;
10755 gboolean originally_enc = FALSE;
10756 gboolean originally_sig = FALSE;
10757 Compose *compose = NULL;
10758 gchar *s_system = NULL;
10760 cm_return_if_fail(msgview != NULL);
10762 cm_return_if_fail(msginfo_list != NULL);
10764 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10765 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10766 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10768 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10769 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10770 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10771 orig_msginfo, mimeinfo);
10772 if (tmp_msginfo != NULL) {
10773 new_msglist = g_slist_append(NULL, tmp_msginfo);
10775 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10776 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10777 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10779 tmp_msginfo->folder = orig_msginfo->folder;
10780 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10781 if (orig_msginfo->tags) {
10782 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10783 tmp_msginfo->folder->tags_dirty = TRUE;
10789 if (!opening_multiple)
10790 body = messageview_get_selection(msgview);
10793 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10794 procmsg_msginfo_free(tmp_msginfo);
10795 g_slist_free(new_msglist);
10797 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10799 if (compose && originally_enc) {
10800 compose_force_encryption(compose, compose->account, FALSE, s_system);
10803 if (compose && originally_sig && compose->account->default_sign_reply) {
10804 compose_force_signing(compose, compose->account, s_system);
10810 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10813 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10814 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10815 GSList *cur = msginfo_list;
10816 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10817 "messages. Opening the windows "
10818 "could take some time. Do you "
10819 "want to continue?"),
10820 g_slist_length(msginfo_list));
10821 if (g_slist_length(msginfo_list) > 9
10822 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10823 != G_ALERTALTERNATE) {
10828 /* We'll open multiple compose windows */
10829 /* let the WM place the next windows */
10830 compose_force_window_origin = FALSE;
10831 for (; cur; cur = cur->next) {
10833 tmplist.data = cur->data;
10834 tmplist.next = NULL;
10835 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10837 compose_force_window_origin = TRUE;
10839 /* forwarding multiple mails as attachments is done via a
10840 * single compose window */
10841 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10845 void compose_set_position(Compose *compose, gint pos)
10847 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10849 gtkut_text_view_set_position(text, pos);
10852 gboolean compose_search_string(Compose *compose,
10853 const gchar *str, gboolean case_sens)
10855 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10857 return gtkut_text_view_search_string(text, str, case_sens);
10860 gboolean compose_search_string_backward(Compose *compose,
10861 const gchar *str, gboolean case_sens)
10863 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10865 return gtkut_text_view_search_string_backward(text, str, case_sens);
10868 /* allocate a msginfo structure and populate its data from a compose data structure */
10869 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10871 MsgInfo *newmsginfo;
10873 gchar buf[BUFFSIZE];
10875 cm_return_val_if_fail( compose != NULL, NULL );
10877 newmsginfo = procmsg_msginfo_new();
10880 get_rfc822_date(buf, sizeof(buf));
10881 newmsginfo->date = g_strdup(buf);
10884 if (compose->from_name) {
10885 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10886 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10890 if (compose->subject_entry)
10891 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10893 /* to, cc, reply-to, newsgroups */
10894 for (list = compose->header_list; list; list = list->next) {
10895 gchar *header = gtk_editable_get_chars(
10897 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10898 gchar *entry = gtk_editable_get_chars(
10899 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10901 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10902 if ( newmsginfo->to == NULL ) {
10903 newmsginfo->to = g_strdup(entry);
10904 } else if (entry && *entry) {
10905 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10906 g_free(newmsginfo->to);
10907 newmsginfo->to = tmp;
10910 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10911 if ( newmsginfo->cc == NULL ) {
10912 newmsginfo->cc = g_strdup(entry);
10913 } else if (entry && *entry) {
10914 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10915 g_free(newmsginfo->cc);
10916 newmsginfo->cc = tmp;
10919 if ( strcasecmp(header,
10920 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10921 if ( newmsginfo->newsgroups == NULL ) {
10922 newmsginfo->newsgroups = g_strdup(entry);
10923 } else if (entry && *entry) {
10924 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10925 g_free(newmsginfo->newsgroups);
10926 newmsginfo->newsgroups = tmp;
10934 /* other data is unset */
10940 /* update compose's dictionaries from folder dict settings */
10941 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10942 FolderItem *folder_item)
10944 cm_return_if_fail(compose != NULL);
10946 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10947 FolderItemPrefs *prefs = folder_item->prefs;
10949 if (prefs->enable_default_dictionary)
10950 gtkaspell_change_dict(compose->gtkaspell,
10951 prefs->default_dictionary, FALSE);
10952 if (folder_item->prefs->enable_default_alt_dictionary)
10953 gtkaspell_change_alt_dict(compose->gtkaspell,
10954 prefs->default_alt_dictionary);
10955 if (prefs->enable_default_dictionary
10956 || prefs->enable_default_alt_dictionary)
10957 compose_spell_menu_changed(compose);