2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
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/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
34 #ifdef GDK_WINDOWING_X11
38 #include <pango/pango-break.h>
43 #include <sys/types.h>
48 # include <sys/wait.h>
52 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
59 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
66 #include "mainwindow.h"
68 #ifndef USE_ALT_ADDRBOOK
69 #include "addressbook.h"
71 #include "addressbook-dbus.h"
72 #include "addressadd.h"
74 #include "folderview.h"
77 #include "stock_pixmap.h"
78 #include "send_message.h"
81 #include "customheader.h"
82 #include "prefs_common.h"
83 #include "prefs_account.h"
87 #include "procheader.h"
89 #include "statusbar.h"
91 #include "quoted-printable.h"
95 #include "gtkshruler.h"
97 #include "alertpanel.h"
98 #include "manage_window.h"
100 #include "folder_item_prefs.h"
101 #include "addr_compl.h"
102 #include "quote_fmt.h"
104 #include "foldersel.h"
107 #include "message_search.h"
108 #include "combobox.h"
112 #include "autofaces.h"
113 #include "spell_entry.h"
115 #include "file-utils.h"
118 #include "password.h"
119 #include "ldapserver.h"
133 #define N_ATTACH_COLS (N_COL_COLUMNS)
137 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
= -1,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
= 0,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
141 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
147 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
148 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
149 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
152 } ComposeCallAdvancedAction
;
156 PRIORITY_HIGHEST
= 1,
165 COMPOSE_INSERT_SUCCESS
,
166 COMPOSE_INSERT_READ_ERROR
,
167 COMPOSE_INSERT_INVALID_CHARACTER
,
168 COMPOSE_INSERT_NO_FILE
169 } ComposeInsertResult
;
173 COMPOSE_WRITE_FOR_SEND
,
174 COMPOSE_WRITE_FOR_STORE
179 COMPOSE_QUOTE_FORCED
,
186 SUBJECT_FIELD_PRESENT
,
191 #define B64_LINE_SIZE 57
192 #define B64_BUFFSIZE 77
194 #define MAX_REFERENCES_LEN 999
196 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
197 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
199 #define COMPOSE_PRIVACY_WARNING() { \
200 alertpanel_error(_("You have opted to sign and/or encrypt this " \
201 "message but have not selected a privacy system.\n\n" \
202 "Signing and encrypting have been disabled for this " \
207 #define INVALID_PID INVALID_HANDLE_VALUE
209 #define INVALID_PID -1
212 static GdkRGBA default_header_bgcolor
=
215 static GdkRGBA default_header_color
=
218 static GList
*compose_list
= NULL
;
219 static GSList
*extra_headers
= NULL
;
221 static Compose
*compose_generic_new (PrefsAccount
*account
,
225 GList
*listAddress
);
227 static Compose
*compose_create (PrefsAccount
*account
,
232 static void compose_entry_indicate (Compose
*compose
,
233 const gchar
*address
);
234 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
235 ComposeQuoteMode quote_mode
,
239 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
240 GSList
*msginfo_list
);
241 static Compose
*compose_reply (MsgInfo
*msginfo
,
242 ComposeQuoteMode quote_mode
,
247 static Compose
*compose_reply_mode (ComposeMode mode
,
248 GSList
*msginfo_list
,
250 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
251 static void compose_update_privacy_systems_menu(Compose
*compose
);
253 static GtkWidget
*compose_account_option_menu_create
255 static void compose_set_out_encoding (Compose
*compose
);
256 static void compose_set_template_menu (Compose
*compose
);
257 static void compose_destroy (Compose
*compose
);
259 static MailField
compose_entries_set (Compose
*compose
,
261 ComposeEntryType to_type
);
262 static gint
compose_parse_header (Compose
*compose
,
264 static gint
compose_parse_manual_headers (Compose
*compose
,
266 HeaderEntry
*entries
);
267 static gchar
*compose_parse_references (const gchar
*ref
,
270 static gchar
*compose_quote_fmt (Compose
*compose
,
276 gboolean need_unescape
,
277 const gchar
*err_msg
);
279 static void compose_reply_set_entry (Compose
*compose
,
285 followup_and_reply_to
);
286 static void compose_reedit_set_entry (Compose
*compose
,
289 static void compose_insert_sig (Compose
*compose
,
291 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
294 static gboolean
compose_attach_append (Compose
*compose
,
297 const gchar
*content_type
,
298 const gchar
*charset
);
299 static void compose_attach_parts (Compose
*compose
,
302 static gboolean
compose_beautify_paragraph (Compose
*compose
,
303 GtkTextIter
*par_iter
,
305 static void compose_wrap_all (Compose
*compose
);
306 static void compose_wrap_all_full (Compose
*compose
,
309 static void compose_set_title (Compose
*compose
);
310 static void compose_select_account (Compose
*compose
,
311 PrefsAccount
*account
,
314 static PrefsAccount
*compose_current_mail_account(void);
315 /* static gint compose_send (Compose *compose); */
316 static gboolean compose_check_for_valid_recipient
318 static gboolean
compose_check_entries (Compose
*compose
,
319 gboolean check_everything
);
320 static gint
compose_write_to_file (Compose
*compose
,
323 gboolean attach_parts
);
324 static gint
compose_write_body_to_file (Compose
*compose
,
326 static gint
compose_remove_reedit_target (Compose
*compose
,
328 static void compose_remove_draft (Compose
*compose
);
329 static ComposeQueueResult
compose_queue_sub (Compose
*compose
,
333 gboolean perform_checks
,
334 gboolean remove_reedit_target
);
335 static int compose_add_attachments (Compose
*compose
,
337 static gchar
*compose_get_header (Compose
*compose
);
338 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
340 static void compose_convert_header (Compose
*compose
,
345 gboolean addr_field
);
347 static void compose_attach_info_free (AttachInfo
*ainfo
);
348 static void compose_attach_remove_selected (GtkAction
*action
,
351 static void compose_template_apply (Compose
*compose
,
354 static void compose_attach_property (GtkAction
*action
,
356 static void compose_attach_property_create (gboolean
*cancelled
);
357 static void attach_property_ok (GtkWidget
*widget
,
358 gboolean
*cancelled
);
359 static void attach_property_cancel (GtkWidget
*widget
,
360 gboolean
*cancelled
);
361 static gint
attach_property_delete_event (GtkWidget
*widget
,
363 gboolean
*cancelled
);
364 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
366 gboolean
*cancelled
);
368 static void compose_exec_ext_editor (Compose
*compose
);
369 static gboolean
compose_ext_editor_kill (Compose
*compose
);
370 static void compose_ext_editor_closed_cb (GPid pid
,
373 static void compose_set_ext_editor_sensitive (Compose
*compose
,
375 static gboolean
compose_get_ext_editor_cmd_valid();
376 static gboolean
compose_get_ext_editor_uses_socket();
378 static gboolean compose_ext_editor_plug_removed_cb
381 #endif /* G_OS_WIN32 */
383 static void compose_undo_state_changed (UndoMain
*undostruct
,
388 static void compose_create_header_entry (Compose
*compose
);
389 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
390 gchar
*text
, ComposePrefType pref_type
);
391 static void compose_remove_header_entries(Compose
*compose
);
393 static void compose_update_priority_menu_item(Compose
* compose
);
395 static void compose_spell_menu_changed (void *data
);
396 static void compose_dict_changed (void *data
);
398 static void compose_add_field_list ( Compose
*compose
,
399 GList
*listAddress
);
401 /* callback functions */
403 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
404 GtkAllocation
*allocation
,
406 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
407 GtkAllocation
*allocation
,
408 GtkSHRuler
*shruler
);
409 static void account_activated (GtkComboBox
*optmenu
,
411 static void attach_selected (GtkTreeView
*tree_view
,
412 GtkTreePath
*tree_path
,
413 GtkTreeViewColumn
*column
,
415 static gboolean
attach_button_pressed (GtkWidget
*widget
,
416 GdkEventButton
*event
,
418 static gboolean
attach_key_pressed (GtkWidget
*widget
,
421 static void compose_send_cb (GtkAction
*action
, gpointer data
);
422 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
424 static void compose_save_cb (GtkAction
*action
,
427 static void compose_attach_cb (GtkAction
*action
,
429 static void compose_insert_file_cb (GtkAction
*action
,
431 static void compose_insert_sig_cb (GtkAction
*action
,
433 static void compose_replace_sig_cb (GtkAction
*action
,
436 static void compose_close_cb (GtkAction
*action
,
438 static void compose_print_cb (GtkAction
*action
,
441 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
443 static void compose_address_cb (GtkAction
*action
,
445 static void about_show_cb (GtkAction
*action
,
447 static void compose_template_activate_cb(GtkWidget
*widget
,
450 static void compose_ext_editor_cb (GtkAction
*action
,
453 static gint
compose_delete_cb (GtkWidget
*widget
,
457 static void compose_undo_cb (GtkAction
*action
,
459 static void compose_redo_cb (GtkAction
*action
,
461 static void compose_cut_cb (GtkAction
*action
,
463 static void compose_copy_cb (GtkAction
*action
,
465 static void compose_paste_cb (GtkAction
*action
,
467 static void compose_paste_as_quote_cb (GtkAction
*action
,
469 static void compose_paste_no_wrap_cb (GtkAction
*action
,
471 static void compose_paste_wrap_cb (GtkAction
*action
,
473 static void compose_allsel_cb (GtkAction
*action
,
476 static void compose_advanced_action_cb (GtkAction
*action
,
479 static void compose_grab_focus_cb (GtkWidget
*widget
,
482 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
485 static void compose_wrap_cb (GtkAction
*action
,
487 static void compose_wrap_all_cb (GtkAction
*action
,
489 static void compose_find_cb (GtkAction
*action
,
491 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
493 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
496 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
498 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
500 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
502 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
503 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
504 static void compose_activate_privacy_system (Compose
*compose
,
505 PrefsAccount
*account
,
507 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
);
508 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
510 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
512 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
513 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
514 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
516 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
517 GdkDragContext
*drag_context
,
520 GtkSelectionData
*data
,
524 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
525 GdkDragContext
*drag_context
,
528 GtkSelectionData
*data
,
532 static void compose_header_drag_received_cb (GtkWidget
*widget
,
533 GdkDragContext
*drag_context
,
536 GtkSelectionData
*data
,
541 static gboolean
compose_drag_drop (GtkWidget
*widget
,
542 GdkDragContext
*drag_context
,
544 guint time
, gpointer user_data
);
545 static gboolean completion_set_focus_to_subject
550 static void text_inserted (GtkTextBuffer
*buffer
,
555 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
556 ComposeQuoteMode quote_mode
,
560 gboolean followup_and_reply_to
,
563 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
564 ComposeHeaderEntry
*headerentry
);
565 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
567 ComposeHeaderEntry
*headerentry
);
568 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
569 ComposeHeaderEntry
*headerentry
);
571 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
573 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
575 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
581 static void compose_check_all (GtkAction
*action
, gpointer data
);
582 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
583 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
584 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
587 static PrefsAccount
*compose_find_account (MsgInfo
*msginfo
);
589 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
592 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
593 FolderItem
*folder_item
);
595 static void compose_attach_update_label(Compose
*compose
);
596 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
597 gboolean respect_default_to
);
598 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
599 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
);
601 static GtkActionEntry compose_popup_entries
[] =
603 {"Compose", NULL
, "Compose", NULL
, NULL
, NULL
},
604 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
605 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
606 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
607 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
610 static GtkActionEntry compose_entries
[] =
612 {"Menu", NULL
, "Menu", NULL
, NULL
, NULL
},
614 {"Message", NULL
, N_("_Message"), NULL
, NULL
, NULL
},
615 {"Edit", NULL
, N_("_Edit"), NULL
, NULL
, NULL
},
617 {"Spelling", NULL
, N_("_Spelling"), NULL
, NULL
, NULL
},
619 {"Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
620 {"Tools", NULL
, N_("_Tools"), NULL
, NULL
, NULL
},
621 {"Help", NULL
, N_("_Help"), NULL
, NULL
, NULL
},
623 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
624 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
625 {"Message/---", NULL
, "---", NULL
, NULL
, NULL
},
627 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
628 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
629 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
630 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
631 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
632 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
633 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
634 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
635 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
636 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
639 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
640 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
641 {"Edit/---", NULL
, "---", NULL
, NULL
, NULL
},
643 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
644 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
645 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
647 {"Edit/SpecialPaste", NULL
, N_("_Special paste"), NULL
, NULL
, NULL
},
648 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
649 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
650 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
652 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
654 {"Edit/Advanced", NULL
, N_("A_dvanced"), NULL
, NULL
, NULL
},
655 {"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*/
656 {"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*/
657 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
658 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
659 {"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*/
660 {"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*/
661 {"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*/
662 {"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*/
663 {"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*/
664 {"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*/
665 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
666 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
667 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
668 {"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*/
670 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
671 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
673 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
674 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
675 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
676 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
677 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
680 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
681 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
682 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
683 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
685 {"Spelling/---", NULL
, "---", NULL
, NULL
, NULL
},
686 {"Spelling/Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
690 {"Options/ReplyMode", NULL
, N_("Reply _mode"), NULL
, NULL
, NULL
},
691 {"Options/---", NULL
, "---", NULL
, NULL
, NULL
},
692 {"Options/PrivacySystem", NULL
, N_("Privacy _System"), NULL
, NULL
, NULL
},
693 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
695 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
696 {"Options/Priority", NULL
, N_("_Priority"), NULL
, NULL
, NULL
},
698 {"Options/Encoding", NULL
, N_("Character _encoding"), NULL
, NULL
, NULL
},
699 {"Options/Encoding/---", NULL
, "---", NULL
, NULL
, NULL
},
700 #define ENC_ACTION(cs_char,c_char,string) \
701 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
703 {"Options/Encoding/Western", NULL
, N_("Western European"), NULL
, NULL
, NULL
},
704 {"Options/Encoding/Baltic", NULL
, N_("Baltic"), NULL
, NULL
, NULL
},
705 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew"), NULL
, NULL
, NULL
},
706 {"Options/Encoding/Arabic", NULL
, N_("Arabic"), NULL
, NULL
, NULL
},
707 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic"), NULL
, NULL
, NULL
},
708 {"Options/Encoding/Japanese", NULL
, N_("Japanese"), NULL
, NULL
, NULL
},
709 {"Options/Encoding/Chinese", NULL
, N_("Chinese"), NULL
, NULL
, NULL
},
710 {"Options/Encoding/Korean", NULL
, N_("Korean"), NULL
, NULL
, NULL
},
711 {"Options/Encoding/Thai", NULL
, N_("Thai"), NULL
, NULL
, NULL
},
714 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
716 {"Tools/Template", NULL
, N_("_Template"), NULL
, NULL
, NULL
},
717 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
718 {"Tools/Actions", NULL
, N_("Actio_ns"), NULL
, NULL
, NULL
},
719 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
722 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
725 static GtkToggleActionEntry compose_toggle_entries
[] =
727 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
), FALSE
}, /* Toggle */
728 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
), FALSE
}, /* Toggle */
729 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
), FALSE
}, /* Toggle */
730 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
), FALSE
}, /* Toggle */
731 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
), FALSE
}, /* Toggle */
732 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
), FALSE
}, /* Toggle */
733 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
), FALSE
}, /* Toggle */
736 static GtkRadioActionEntry compose_radio_rm_entries
[] =
738 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
739 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
740 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
741 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
744 static GtkRadioActionEntry compose_radio_prio_entries
[] =
746 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
747 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
748 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
749 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
750 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
753 static GtkRadioActionEntry compose_radio_enc_entries
[] =
755 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Cyrillic/"CS_MACCYR
, C_MACCYR
, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
780 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
781 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
782 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
783 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
784 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
785 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
786 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
787 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
790 static GtkTargetEntry compose_mime_types
[] =
792 {"text/uri-list", 0, 0},
793 {"UTF8_STRING", 0, 0},
797 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
799 const GList
*compose_list
= compose_get_compose_list();
800 const GList
*elem
= NULL
;
803 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
805 Compose
*c
= (Compose
*)elem
->data
;
807 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
811 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
812 gtkut_window_popup(c
->window
);
820 static GdkRGBA quote_color1
=
822 static GdkRGBA quote_color2
=
824 static GdkRGBA quote_color3
=
827 static GdkRGBA quote_bgcolor1
=
829 static GdkRGBA quote_bgcolor2
=
831 static GdkRGBA quote_bgcolor3
=
834 static GdkRGBA signature_color
=
837 static GdkRGBA uri_color
=
840 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
842 GtkTextBuffer
*buffer
;
843 GdkRGBA black
= { 0, 0, 0, 1 };
845 buffer
= gtk_text_view_get_buffer(text
);
847 if (prefs_common
.enable_color
) {
848 /* grab the quote colors, converting from an int to a GdkColor */
849 quote_color1
= prefs_common
.color
[COL_QUOTE_LEVEL1
];
850 quote_color2
= prefs_common
.color
[COL_QUOTE_LEVEL2
];
851 quote_color3
= prefs_common
.color
[COL_QUOTE_LEVEL3
];
852 quote_bgcolor1
= prefs_common
.color
[COL_QUOTE_LEVEL1_BG
];
853 quote_bgcolor2
= prefs_common
.color
[COL_QUOTE_LEVEL2_BG
];
854 quote_bgcolor3
= prefs_common
.color
[COL_QUOTE_LEVEL3_BG
];
855 signature_color
= prefs_common
.color
[COL_SIGNATURE
];
856 uri_color
= prefs_common
.color
[COL_URI
];
858 signature_color
= quote_color1
= quote_color2
= quote_color3
=
859 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
862 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
863 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
864 "foreground-rgba", "e_color1
,
865 "paragraph-background-rgba", "e_bgcolor1
,
867 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
868 "foreground-rgba", "e_color2
,
869 "paragraph-background-rgba", "e_bgcolor2
,
871 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
872 "foreground-rgba", "e_color3
,
873 "paragraph-background-rgba", "e_bgcolor3
,
876 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
877 "foreground-rgba", "e_color1
,
879 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
880 "foreground-rgba", "e_color2
,
882 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
883 "foreground-rgba", "e_color3
,
887 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
888 "foreground-rgba", &signature_color
,
891 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
892 "foreground-rgba", &uri_color
,
894 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
895 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
898 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
901 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
904 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
906 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
909 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
911 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
914 #define SCROLL_TO_CURSOR(compose) { \
915 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
916 gtk_text_view_get_buffer( \
917 GTK_TEXT_VIEW(compose->text))); \
918 gtk_text_view_scroll_mark_onscreen( \
919 GTK_TEXT_VIEW(compose->text), \
923 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
926 if (folderidentifier
) {
927 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
928 prefs_common
.compose_save_to_history
= add_history(
929 prefs_common
.compose_save_to_history
, folderidentifier
);
930 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
931 prefs_common
.compose_save_to_history
);
934 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
935 if (folderidentifier
)
936 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
938 gtk_entry_set_text(GTK_ENTRY(entry
), "");
941 static gchar
*compose_get_save_to(Compose
*compose
)
944 gchar
*result
= NULL
;
945 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
946 result
= gtk_editable_get_chars(entry
, 0, -1);
949 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
950 prefs_common
.compose_save_to_history
= add_history(
951 prefs_common
.compose_save_to_history
, result
);
952 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
953 prefs_common
.compose_save_to_history
);
958 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
959 GList
*attach_files
, GList
*listAddress
)
962 GtkTextView
*textview
;
963 GtkTextBuffer
*textbuf
;
965 const gchar
*subject_format
= NULL
;
966 const gchar
*body_format
= NULL
;
967 gchar
*mailto_from
= NULL
;
968 PrefsAccount
*mailto_account
= NULL
;
969 MsgInfo
* dummyinfo
= NULL
;
970 gint cursor_pos
= -1;
971 MailField mfield
= NO_FIELD_PRESENT
;
975 /* check if mailto defines a from */
976 if (mailto
&& *mailto
!= '\0') {
977 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
978 /* mailto defines a from, check if we can get account prefs from it,
979 if not, the account prefs will be guessed using other ways, but we'll keep
982 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
983 if (mailto_account
== NULL
) {
985 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
986 extract_address(tmp_from
);
987 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
991 account
= mailto_account
;
994 /* if no account prefs set from mailto, set if from folder prefs (if any) */
995 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
996 account
= account_find_from_id(item
->prefs
->default_account
);
998 /* if no account prefs set, fallback to the current one */
999 if (!account
) account
= cur_account
;
1000 cm_return_val_if_fail(account
!= NULL
, NULL
);
1002 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1003 compose_apply_folder_privacy_settings(compose
, item
);
1005 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1006 (account
->default_encrypt
|| account
->default_sign
))
1007 COMPOSE_PRIVACY_WARNING();
1009 /* override from name if mailto asked for it */
1011 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1012 g_free(mailto_from
);
1014 /* override from name according to folder properties */
1015 if (item
&& item
->prefs
&&
1016 item
->prefs
->compose_with_format
&&
1017 item
->prefs
->compose_override_from_format
&&
1018 *item
->prefs
->compose_override_from_format
!= '\0') {
1023 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1025 /* decode \-escape sequences in the internal representation of the quote format */
1026 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1027 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1030 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1031 compose
->gtkaspell
);
1033 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1035 quote_fmt_scan_string(tmp
);
1038 buf
= quote_fmt_get_buffer();
1040 alertpanel_error(_("New message From format error."));
1042 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1043 quote_fmt_reset_vartable();
1044 quote_fmtlex_destroy();
1049 compose
->replyinfo
= NULL
;
1050 compose
->fwdinfo
= NULL
;
1052 textview
= GTK_TEXT_VIEW(compose
->text
);
1053 textbuf
= gtk_text_view_get_buffer(textview
);
1054 compose_create_tags(textview
, compose
);
1056 undo_block(compose
->undostruct
);
1058 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1061 if (account
->auto_sig
)
1062 compose_insert_sig(compose
, FALSE
);
1063 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1064 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1066 if (account
->protocol
!= A_NNTP
) {
1067 if (mailto
&& *mailto
!= '\0') {
1068 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1071 compose_set_folder_prefs(compose
, item
, TRUE
);
1073 if (item
&& item
->ret_rcpt
) {
1074 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1077 if (mailto
&& *mailto
!= '\0') {
1078 if (!strchr(mailto
, '@'))
1079 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1081 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1082 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1083 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1084 mfield
= TO_FIELD_PRESENT
;
1087 * CLAWS: just don't allow return receipt request, even if the user
1088 * may want to send an email. simple but foolproof.
1090 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1092 compose_add_field_list( compose
, listAddress
);
1094 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1095 subject_format
= item
->prefs
->compose_subject_format
;
1096 body_format
= item
->prefs
->compose_body_format
;
1097 } else if (account
->compose_with_format
) {
1098 subject_format
= account
->compose_subject_format
;
1099 body_format
= account
->compose_body_format
;
1100 } else if (prefs_common
.compose_with_format
) {
1101 subject_format
= prefs_common
.compose_subject_format
;
1102 body_format
= prefs_common
.compose_body_format
;
1105 if (subject_format
|| body_format
) {
1108 && *subject_format
!= '\0' )
1110 gchar
*subject
= NULL
;
1115 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1117 /* decode \-escape sequences in the internal representation of the quote format */
1118 tmp
= g_malloc(strlen(subject_format
)+1);
1119 pref_get_unescaped_pref(tmp
, subject_format
);
1121 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1123 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1124 compose
->gtkaspell
);
1126 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1128 quote_fmt_scan_string(tmp
);
1131 buf
= quote_fmt_get_buffer();
1133 alertpanel_error(_("New message subject format error."));
1135 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1136 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1137 quote_fmt_reset_vartable();
1138 quote_fmtlex_destroy();
1142 mfield
= SUBJECT_FIELD_PRESENT
;
1146 && *body_format
!= '\0' )
1149 GtkTextBuffer
*buffer
;
1150 GtkTextIter start
, end
;
1154 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1156 text
= GTK_TEXT_VIEW(compose
->text
);
1157 buffer
= gtk_text_view_get_buffer(text
);
1158 gtk_text_buffer_get_start_iter(buffer
, &start
);
1159 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1160 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1162 compose_quote_fmt(compose
, dummyinfo
,
1164 NULL
, tmp
, FALSE
, TRUE
,
1165 _("The body of the \"New message\" template has an error at line %d."));
1166 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1167 quote_fmt_reset_vartable();
1171 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1172 gtkaspell_highlight_all(compose
->gtkaspell
);
1174 mfield
= BODY_FIELD_PRESENT
;
1178 procmsg_msginfo_free( &dummyinfo
);
1184 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1185 ainfo
= (AttachInfo
*) curr
->data
;
1187 compose_insert_file(compose
, ainfo
->file
);
1189 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1190 ainfo
->content_type
, ainfo
->charset
);
1194 compose_show_first_last_header(compose
, TRUE
);
1196 /* Set save folder */
1197 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1198 gchar
*folderidentifier
;
1200 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1201 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1202 folderidentifier
= folder_item_get_identifier(item
);
1203 compose_set_save_to(compose
, folderidentifier
);
1204 g_free(folderidentifier
);
1207 /* Place cursor according to provided input (mfield) */
1209 case NO_FIELD_PRESENT
:
1210 if (compose
->header_last
)
1211 gtk_widget_grab_focus(compose
->header_last
->entry
);
1213 case TO_FIELD_PRESENT
:
1214 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1216 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1219 gtk_widget_grab_focus(compose
->subject_entry
);
1221 case SUBJECT_FIELD_PRESENT
:
1222 textview
= GTK_TEXT_VIEW(compose
->text
);
1225 textbuf
= gtk_text_view_get_buffer(textview
);
1228 mark
= gtk_text_buffer_get_insert(textbuf
);
1229 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1230 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1232 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1233 * only defers where it comes to the variable body
1234 * is not null. If no body is present compose->text
1235 * will be null in which case you cannot place the
1236 * cursor inside the component so. An empty component
1237 * is therefore created before placing the cursor
1239 case BODY_FIELD_PRESENT
:
1240 cursor_pos
= quote_fmt_get_cursor_pos();
1241 if (cursor_pos
== -1)
1242 gtk_widget_grab_focus(compose
->header_last
->entry
);
1244 gtk_widget_grab_focus(compose
->text
);
1248 undo_unblock(compose
->undostruct
);
1250 if (prefs_common
.auto_exteditor
)
1251 compose_exec_ext_editor(compose
);
1253 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1255 SCROLL_TO_CURSOR(compose
);
1257 compose
->modified
= FALSE
;
1258 compose_set_title(compose
);
1260 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1265 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1266 gboolean override_pref
, const gchar
*system
)
1268 const gchar
*privacy
= NULL
;
1270 cm_return_if_fail(compose
!= NULL
);
1271 cm_return_if_fail(account
!= NULL
);
1273 if (privacy_system_can_encrypt(compose
->privacy_system
) == FALSE
||
1274 (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
))
1277 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1278 privacy
= account
->default_privacy_system
;
1282 GSList
*privacy_avail
= privacy_get_system_ids();
1283 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1284 privacy
= (gchar
*)(privacy_avail
->data
);
1286 g_slist_free_full(privacy_avail
, g_free
);
1288 if (privacy
!= NULL
) {
1290 g_free(compose
->privacy_system
);
1291 compose
->privacy_system
= NULL
;
1292 g_free(compose
->encdata
);
1293 compose
->encdata
= NULL
;
1295 if (compose
->privacy_system
== NULL
)
1296 compose
->privacy_system
= g_strdup(privacy
);
1297 else if (*(compose
->privacy_system
) == '\0') {
1298 g_free(compose
->privacy_system
);
1299 g_free(compose
->encdata
);
1300 compose
->encdata
= NULL
;
1301 compose
->privacy_system
= g_strdup(privacy
);
1303 compose_update_privacy_system_menu_item(compose
, FALSE
);
1304 compose_use_encryption(compose
, TRUE
);
1308 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1310 const gchar
*privacy
= NULL
;
1311 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
)
1314 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1315 privacy
= account
->default_privacy_system
;
1319 GSList
*privacy_avail
= privacy_get_system_ids();
1320 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1321 privacy
= (gchar
*)(privacy_avail
->data
);
1325 if (privacy
!= NULL
) {
1327 g_free(compose
->privacy_system
);
1328 compose
->privacy_system
= NULL
;
1329 g_free(compose
->encdata
);
1330 compose
->encdata
= NULL
;
1332 if (compose
->privacy_system
== NULL
)
1333 compose
->privacy_system
= g_strdup(privacy
);
1334 compose_update_privacy_system_menu_item(compose
, FALSE
);
1335 compose_use_signing(compose
, TRUE
);
1339 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1343 Compose
*compose
= NULL
;
1345 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1347 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1348 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1350 list_len
= g_slist_length(msginfo_list
);
1354 case COMPOSE_REPLY_TO_ADDRESS
:
1355 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1356 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1358 case COMPOSE_REPLY_WITH_QUOTE
:
1359 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1360 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1362 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1363 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1364 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1366 case COMPOSE_REPLY_TO_SENDER
:
1367 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1368 FALSE
, FALSE
, TRUE
, body
);
1370 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1371 compose
= compose_followup_and_reply_to(msginfo
,
1372 COMPOSE_QUOTE_CHECK
,
1373 FALSE
, FALSE
, body
);
1375 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1376 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1377 FALSE
, FALSE
, TRUE
, body
);
1379 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1380 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1381 FALSE
, FALSE
, TRUE
, NULL
);
1383 case COMPOSE_REPLY_TO_ALL
:
1384 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1385 TRUE
, FALSE
, FALSE
, body
);
1387 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1388 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1389 TRUE
, FALSE
, FALSE
, body
);
1391 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1392 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1393 TRUE
, FALSE
, FALSE
, NULL
);
1395 case COMPOSE_REPLY_TO_LIST
:
1396 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1397 FALSE
, TRUE
, FALSE
, body
);
1399 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1400 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1401 FALSE
, TRUE
, FALSE
, body
);
1403 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1404 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1405 FALSE
, TRUE
, FALSE
, NULL
);
1407 case COMPOSE_FORWARD
:
1408 if (prefs_common
.forward_as_attachment
) {
1409 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1412 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1416 case COMPOSE_FORWARD_INLINE
:
1417 /* check if we reply to more than one Message */
1418 if (list_len
== 1) {
1419 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1422 /* more messages FALL THROUGH */
1423 case COMPOSE_FORWARD_AS_ATTACH
:
1424 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1426 case COMPOSE_REDIRECT
:
1427 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1430 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1433 if (compose
== NULL
) {
1434 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1438 compose
->rmode
= mode
;
1439 switch (compose
->rmode
) {
1441 case COMPOSE_REPLY_WITH_QUOTE
:
1442 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1443 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1444 debug_print("reply mode Normal\n");
1445 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1446 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1448 case COMPOSE_REPLY_TO_SENDER
:
1449 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1450 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1451 debug_print("reply mode Sender\n");
1452 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1454 case COMPOSE_REPLY_TO_ALL
:
1455 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1456 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1457 debug_print("reply mode All\n");
1458 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1460 case COMPOSE_REPLY_TO_LIST
:
1461 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1462 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1463 debug_print("reply mode List\n");
1464 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1466 case COMPOSE_REPLY_TO_ADDRESS
:
1467 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1475 static Compose
*compose_reply(MsgInfo
*msginfo
,
1476 ComposeQuoteMode quote_mode
,
1482 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1483 to_sender
, FALSE
, body
);
1486 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1487 ComposeQuoteMode quote_mode
,
1492 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1493 to_sender
, TRUE
, body
);
1496 static void compose_extract_original_charset(Compose
*compose
)
1498 MsgInfo
*info
= NULL
;
1499 if (compose
->replyinfo
) {
1500 info
= compose
->replyinfo
;
1501 } else if (compose
->fwdinfo
) {
1502 info
= compose
->fwdinfo
;
1503 } else if (compose
->targetinfo
) {
1504 info
= compose
->targetinfo
;
1507 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1508 MimeInfo
*partinfo
= mimeinfo
;
1509 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1510 partinfo
= procmime_mimeinfo_next(partinfo
);
1512 compose
->orig_charset
=
1513 g_strdup(procmime_mimeinfo_get_parameter(
1514 partinfo
, "charset"));
1516 procmime_mimeinfo_free_all(&mimeinfo
);
1520 #define SIGNAL_BLOCK(buffer) { \
1521 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1522 G_CALLBACK(compose_changed_cb), \
1524 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1525 G_CALLBACK(text_inserted), \
1529 #define SIGNAL_UNBLOCK(buffer) { \
1530 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1531 G_CALLBACK(compose_changed_cb), \
1533 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1534 G_CALLBACK(text_inserted), \
1538 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1539 ComposeQuoteMode quote_mode
,
1540 gboolean to_all
, gboolean to_ml
,
1542 gboolean followup_and_reply_to
,
1546 PrefsAccount
*account
= NULL
;
1547 GtkTextView
*textview
;
1548 GtkTextBuffer
*textbuf
;
1549 gboolean quote
= FALSE
;
1550 const gchar
*qmark
= NULL
;
1551 const gchar
*body_fmt
= NULL
;
1552 gchar
*s_system
= NULL
;
1554 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1555 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1557 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1559 cm_return_val_if_fail(account
!= NULL
, NULL
);
1561 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1562 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1564 compose
->updating
= TRUE
;
1566 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1567 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1569 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1570 if (!compose
->replyinfo
)
1571 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1573 compose_extract_original_charset(compose
);
1575 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1576 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1578 /* Set save folder */
1579 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1580 gchar
*folderidentifier
;
1582 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1583 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1584 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1585 compose_set_save_to(compose
, folderidentifier
);
1586 g_free(folderidentifier
);
1589 if (compose_parse_header(compose
, msginfo
) < 0) {
1590 compose
->updating
= FALSE
;
1591 compose_destroy(compose
);
1595 /* override from name according to folder properties */
1596 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1597 msginfo
->folder
->prefs
->reply_with_format
&&
1598 msginfo
->folder
->prefs
->reply_override_from_format
&&
1599 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1604 /* decode \-escape sequences in the internal representation of the quote format */
1605 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1606 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1609 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1610 compose
->gtkaspell
);
1612 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1614 quote_fmt_scan_string(tmp
);
1617 buf
= quote_fmt_get_buffer();
1619 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1621 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1622 quote_fmt_reset_vartable();
1623 quote_fmtlex_destroy();
1628 textview
= (GTK_TEXT_VIEW(compose
->text
));
1629 textbuf
= gtk_text_view_get_buffer(textview
);
1630 compose_create_tags(textview
, compose
);
1632 undo_block(compose
->undostruct
);
1634 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1635 gtkaspell_block_check(compose
->gtkaspell
);
1638 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1639 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1640 /* use the reply format of folder (if enabled), or the account's one
1641 (if enabled) or fallback to the global reply format, which is always
1642 enabled (even if empty), and use the relevant quotemark */
1644 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1645 msginfo
->folder
->prefs
->reply_with_format
) {
1646 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1647 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1649 } else if (account
->reply_with_format
) {
1650 qmark
= account
->reply_quotemark
;
1651 body_fmt
= account
->reply_body_format
;
1654 qmark
= prefs_common
.quotemark
;
1655 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1656 body_fmt
= gettext(prefs_common
.quotefmt
);
1663 /* empty quotemark is not allowed */
1664 if (qmark
== NULL
|| *qmark
== '\0')
1666 compose_quote_fmt(compose
, compose
->replyinfo
,
1667 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1668 _("The body of the \"Reply\" template has an error at line %d."));
1669 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1670 quote_fmt_reset_vartable();
1673 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1674 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1677 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1678 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1679 compose_force_signing(compose
, account
, s_system
);
1683 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1684 ((account
->default_encrypt
|| account
->default_sign
) ||
1685 (account
->default_encrypt_reply
&& MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) ||
1686 (account
->default_sign_reply
&& MSG_IS_SIGNED(compose
->replyinfo
->flags
))))
1687 COMPOSE_PRIVACY_WARNING();
1689 SIGNAL_BLOCK(textbuf
);
1691 if (account
->auto_sig
)
1692 compose_insert_sig(compose
, FALSE
);
1694 compose_wrap_all(compose
);
1697 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1698 gtkaspell_highlight_all(compose
->gtkaspell
);
1699 gtkaspell_unblock_check(compose
->gtkaspell
);
1701 SIGNAL_UNBLOCK(textbuf
);
1703 gtk_widget_grab_focus(compose
->text
);
1705 undo_unblock(compose
->undostruct
);
1707 if (prefs_common
.auto_exteditor
)
1708 compose_exec_ext_editor(compose
);
1710 compose
->modified
= FALSE
;
1711 compose_set_title(compose
);
1713 compose
->updating
= FALSE
;
1714 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1715 SCROLL_TO_CURSOR(compose
);
1717 if (compose
->deferred_destroy
) {
1718 compose_destroy(compose
);
1726 #define INSERT_FW_HEADER(var, hdr) \
1727 if (msginfo->var && *msginfo->var) { \
1728 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1729 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1730 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1733 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1734 gboolean as_attach
, const gchar
*body
,
1735 gboolean no_extedit
,
1739 GtkTextView
*textview
;
1740 GtkTextBuffer
*textbuf
;
1741 gint cursor_pos
= -1;
1744 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1745 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1747 if (!account
&& !(account
= compose_find_account(msginfo
)))
1748 account
= cur_account
;
1750 if (!prefs_common
.forward_as_attachment
)
1751 mode
= COMPOSE_FORWARD_INLINE
;
1753 mode
= COMPOSE_FORWARD
;
1754 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1755 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1757 compose
->updating
= TRUE
;
1758 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1759 if (!compose
->fwdinfo
)
1760 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1762 compose_extract_original_charset(compose
);
1764 if (msginfo
->subject
&& *msginfo
->subject
) {
1765 gchar
*buf
, *buf2
, *p
;
1767 buf
= p
= g_strdup(msginfo
->subject
);
1768 p
+= subject_get_prefix_length(p
);
1769 memmove(buf
, p
, strlen(p
) + 1);
1771 buf2
= g_strdup_printf("Fw: %s", buf
);
1772 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1778 /* override from name according to folder properties */
1779 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1780 msginfo
->folder
->prefs
->forward_with_format
&&
1781 msginfo
->folder
->prefs
->forward_override_from_format
&&
1782 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1786 MsgInfo
*full_msginfo
= NULL
;
1789 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1791 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1793 /* decode \-escape sequences in the internal representation of the quote format */
1794 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1795 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1798 gtkaspell_block_check(compose
->gtkaspell
);
1799 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1800 compose
->gtkaspell
);
1802 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1804 quote_fmt_scan_string(tmp
);
1807 buf
= quote_fmt_get_buffer();
1809 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1811 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1812 quote_fmt_reset_vartable();
1813 quote_fmtlex_destroy();
1816 procmsg_msginfo_free(&full_msginfo
);
1819 textview
= GTK_TEXT_VIEW(compose
->text
);
1820 textbuf
= gtk_text_view_get_buffer(textview
);
1821 compose_create_tags(textview
, compose
);
1823 undo_block(compose
->undostruct
);
1827 msgfile
= procmsg_get_message_file(msginfo
);
1828 if (!is_file_exist(msgfile
))
1829 g_warning("%s: file does not exist", msgfile
);
1831 compose_attach_append(compose
, msgfile
, msgfile
,
1832 "message/rfc822", NULL
);
1836 const gchar
*qmark
= NULL
;
1837 const gchar
*body_fmt
= NULL
;
1838 MsgInfo
*full_msginfo
;
1840 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1842 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1844 /* use the forward format of folder (if enabled), or the account's one
1845 (if enabled) or fallback to the global forward format, which is always
1846 enabled (even if empty), and use the relevant quotemark */
1847 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1848 msginfo
->folder
->prefs
->forward_with_format
) {
1849 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1850 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1852 } else if (account
->forward_with_format
) {
1853 qmark
= account
->forward_quotemark
;
1854 body_fmt
= account
->forward_body_format
;
1857 qmark
= prefs_common
.fw_quotemark
;
1858 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1859 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1864 /* empty quotemark is not allowed */
1865 if (qmark
== NULL
|| *qmark
== '\0')
1868 compose_quote_fmt(compose
, full_msginfo
,
1869 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1870 _("The body of the \"Forward\" template has an error at line %d."));
1871 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1872 quote_fmt_reset_vartable();
1873 compose_attach_parts(compose
, msginfo
);
1875 procmsg_msginfo_free(&full_msginfo
);
1878 SIGNAL_BLOCK(textbuf
);
1880 if (account
->auto_sig
)
1881 compose_insert_sig(compose
, FALSE
);
1883 compose_wrap_all(compose
);
1886 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1887 gtkaspell_highlight_all(compose
->gtkaspell
);
1888 gtkaspell_unblock_check(compose
->gtkaspell
);
1890 SIGNAL_UNBLOCK(textbuf
);
1892 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1893 (account
->default_encrypt
|| account
->default_sign
))
1894 COMPOSE_PRIVACY_WARNING();
1896 cursor_pos
= quote_fmt_get_cursor_pos();
1897 if (cursor_pos
== -1)
1898 gtk_widget_grab_focus(compose
->header_last
->entry
);
1900 gtk_widget_grab_focus(compose
->text
);
1902 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1903 compose_exec_ext_editor(compose
);
1906 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1907 gchar
*folderidentifier
;
1909 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1910 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1911 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1912 compose_set_save_to(compose
, folderidentifier
);
1913 g_free(folderidentifier
);
1916 undo_unblock(compose
->undostruct
);
1918 compose
->modified
= FALSE
;
1919 compose_set_title(compose
);
1921 compose
->updating
= FALSE
;
1922 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1923 SCROLL_TO_CURSOR(compose
);
1925 if (compose
->deferred_destroy
) {
1926 compose_destroy(compose
);
1930 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1935 #undef INSERT_FW_HEADER
1937 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1940 GtkTextView
*textview
;
1941 GtkTextBuffer
*textbuf
;
1945 gboolean single_mail
= TRUE
;
1947 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1949 if (g_slist_length(msginfo_list
) > 1)
1950 single_mail
= FALSE
;
1952 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1953 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1956 /* guess account from first selected message */
1958 !(account
= compose_find_account(msginfo_list
->data
)))
1959 account
= cur_account
;
1961 cm_return_val_if_fail(account
!= NULL
, NULL
);
1963 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1964 if (msginfo
->data
) {
1965 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1966 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1970 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1971 g_warning("no msginfo_list");
1975 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1976 compose_apply_folder_privacy_settings(compose
, ((MsgInfo
*)msginfo_list
->data
)->folder
);
1977 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1978 (account
->default_encrypt
|| account
->default_sign
))
1979 COMPOSE_PRIVACY_WARNING();
1981 compose
->updating
= TRUE
;
1983 /* override from name according to folder properties */
1984 if (msginfo_list
->data
) {
1985 MsgInfo
*msginfo
= msginfo_list
->data
;
1987 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1988 msginfo
->folder
->prefs
->forward_with_format
&&
1989 msginfo
->folder
->prefs
->forward_override_from_format
&&
1990 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1995 /* decode \-escape sequences in the internal representation of the quote format */
1996 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1997 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2000 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2001 compose
->gtkaspell
);
2003 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2005 quote_fmt_scan_string(tmp
);
2008 buf
= quote_fmt_get_buffer();
2010 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2012 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2013 quote_fmt_reset_vartable();
2014 quote_fmtlex_destroy();
2020 textview
= GTK_TEXT_VIEW(compose
->text
);
2021 textbuf
= gtk_text_view_get_buffer(textview
);
2022 compose_create_tags(textview
, compose
);
2024 undo_block(compose
->undostruct
);
2025 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2026 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2028 if (!is_file_exist(msgfile
))
2029 g_warning("%s: file does not exist", msgfile
);
2031 compose_attach_append(compose
, msgfile
, msgfile
,
2032 "message/rfc822", NULL
);
2037 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2038 if (info
->subject
&& *info
->subject
) {
2039 gchar
*buf
, *buf2
, *p
;
2041 buf
= p
= g_strdup(info
->subject
);
2042 p
+= subject_get_prefix_length(p
);
2043 memmove(buf
, p
, strlen(p
) + 1);
2045 buf2
= g_strdup_printf("Fw: %s", buf
);
2046 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2052 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2053 _("Fw: multiple emails"));
2056 SIGNAL_BLOCK(textbuf
);
2058 if (account
->auto_sig
)
2059 compose_insert_sig(compose
, FALSE
);
2061 compose_wrap_all(compose
);
2063 SIGNAL_UNBLOCK(textbuf
);
2065 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2066 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2068 if (prefs_common
.auto_exteditor
)
2069 compose_exec_ext_editor(compose
);
2071 gtk_widget_grab_focus(compose
->header_last
->entry
);
2072 undo_unblock(compose
->undostruct
);
2073 compose
->modified
= FALSE
;
2074 compose_set_title(compose
);
2076 compose
->updating
= FALSE
;
2077 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2078 SCROLL_TO_CURSOR(compose
);
2080 if (compose
->deferred_destroy
) {
2081 compose_destroy(compose
);
2085 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2090 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2092 GtkTextIter start
= *iter
;
2093 GtkTextIter end_iter
;
2094 int start_pos
= gtk_text_iter_get_offset(&start
);
2096 if (!compose
->account
->sig_sep
)
2099 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2100 start_pos
+strlen(compose
->account
->sig_sep
));
2102 /* check sig separator */
2103 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2104 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2106 /* check end of line (\n) */
2107 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2108 start_pos
+strlen(compose
->account
->sig_sep
));
2109 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2110 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2111 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2112 if (!strcmp(tmp
,"\n")) {
2124 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2126 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2127 Compose
*compose
= (Compose
*)data
;
2128 FolderItem
*old_item
= NULL
;
2129 FolderItem
*new_item
= NULL
;
2130 gchar
*old_id
, *new_id
;
2132 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2133 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2136 old_item
= hookdata
->item
;
2137 new_item
= hookdata
->item2
;
2139 old_id
= folder_item_get_identifier(old_item
);
2140 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2142 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2143 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2144 compose
->targetinfo
->folder
= new_item
;
2147 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2148 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2149 compose
->replyinfo
->folder
= new_item
;
2152 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2153 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2154 compose
->fwdinfo
->folder
= new_item
;
2162 static void compose_colorize_signature(Compose
*compose
)
2164 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2166 GtkTextIter end_iter
;
2167 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2168 while (gtk_text_iter_forward_line(&iter
))
2169 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2170 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2171 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2175 #define BLOCK_WRAP() { \
2176 prev_autowrap = compose->autowrap; \
2177 buffer = gtk_text_view_get_buffer( \
2178 GTK_TEXT_VIEW(compose->text)); \
2179 compose->autowrap = FALSE; \
2181 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2182 G_CALLBACK(compose_changed_cb), \
2184 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2185 G_CALLBACK(text_inserted), \
2188 #define UNBLOCK_WRAP() { \
2189 compose->autowrap = prev_autowrap; \
2190 if (compose->autowrap) { \
2191 gint old = compose->draft_timeout_tag; \
2192 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2193 compose_wrap_all(compose); \
2194 compose->draft_timeout_tag = old; \
2197 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2198 G_CALLBACK(compose_changed_cb), \
2200 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2201 G_CALLBACK(text_inserted), \
2205 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2207 Compose
*compose
= NULL
;
2208 PrefsAccount
*account
= NULL
;
2209 GtkTextView
*textview
;
2210 GtkTextBuffer
*textbuf
;
2214 gboolean use_signing
= FALSE
;
2215 gboolean use_encryption
= FALSE
;
2216 gchar
*privacy_system
= NULL
;
2217 int priority
= PRIORITY_NORMAL
;
2218 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2219 gboolean autowrap
= prefs_common
.autowrap
;
2220 gboolean autoindent
= prefs_common
.auto_indent
;
2221 HeaderEntry
*manual_headers
= NULL
;
2223 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2224 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2226 if (compose_put_existing_to_front(msginfo
)) {
2230 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2231 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2232 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2233 gchar
*queueheader_buf
= NULL
;
2236 /* Select Account from queue headers */
2237 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2238 "X-Claws-Account-Id:")) {
2239 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2240 account
= account_find_from_id(id
);
2241 g_free(queueheader_buf
);
2243 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2244 "X-Sylpheed-Account-Id:")) {
2245 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2246 account
= account_find_from_id(id
);
2247 g_free(queueheader_buf
);
2249 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2251 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2252 account
= account_find_from_id(id
);
2253 g_free(queueheader_buf
);
2255 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2257 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2258 account
= account_find_from_id(id
);
2259 g_free(queueheader_buf
);
2261 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2263 account
= account_find_from_address(queueheader_buf
, FALSE
);
2264 g_free(queueheader_buf
);
2266 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2268 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2269 use_signing
= param
;
2270 g_free(queueheader_buf
);
2272 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2273 "X-Sylpheed-Sign:")) {
2274 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2275 use_signing
= param
;
2276 g_free(queueheader_buf
);
2278 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2279 "X-Claws-Encrypt:")) {
2280 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2281 use_encryption
= param
;
2282 g_free(queueheader_buf
);
2284 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2285 "X-Sylpheed-Encrypt:")) {
2286 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2287 use_encryption
= param
;
2288 g_free(queueheader_buf
);
2290 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2291 "X-Claws-Auto-Wrapping:")) {
2292 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2294 g_free(queueheader_buf
);
2296 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2297 "X-Claws-Auto-Indent:")) {
2298 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2300 g_free(queueheader_buf
);
2302 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2303 "X-Claws-Privacy-System:")) {
2304 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2305 g_free(queueheader_buf
);
2307 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2308 "X-Sylpheed-Privacy-System:")) {
2309 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2310 g_free(queueheader_buf
);
2312 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2314 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2316 g_free(queueheader_buf
);
2318 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2320 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2321 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2322 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2323 if (orig_item
!= NULL
) {
2324 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2329 g_free(queueheader_buf
);
2331 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2333 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2334 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2335 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2336 if (orig_item
!= NULL
) {
2337 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2342 g_free(queueheader_buf
);
2344 /* Get manual headers */
2345 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2346 "X-Claws-Manual-Headers:")) {
2347 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2348 if (listmh
&& *listmh
!= '\0') {
2349 debug_print("Got manual headers: %s\n", listmh
);
2350 manual_headers
= procheader_entries_from_str(listmh
);
2354 g_free(queueheader_buf
);
2357 account
= msginfo
->folder
->folder
->account
;
2360 if (!account
&& prefs_common
.reedit_account_autosel
) {
2362 if (!procheader_get_header_from_msginfo(msginfo
, &from
, "FROM:")) {
2363 extract_address(from
);
2364 account
= account_find_from_address(from
, FALSE
);
2370 account
= cur_account
;
2373 g_warning("can't select account");
2375 procheader_entries_free(manual_headers
);
2379 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2381 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
2382 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
2383 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2384 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2385 compose
->autowrap
= autowrap
;
2386 compose
->replyinfo
= replyinfo
;
2387 compose
->fwdinfo
= fwdinfo
;
2389 compose
->updating
= TRUE
;
2390 compose
->priority
= priority
;
2392 if (privacy_system
!= NULL
) {
2393 compose
->privacy_system
= privacy_system
;
2394 compose_use_signing(compose
, use_signing
);
2395 compose_use_encryption(compose
, use_encryption
);
2396 compose_update_privacy_system_menu_item(compose
, FALSE
);
2398 compose_activate_privacy_system(compose
, account
, FALSE
);
2400 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2401 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
2402 (account
->default_encrypt
|| account
->default_sign
))
2403 COMPOSE_PRIVACY_WARNING();
2405 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2406 compose
->targetinfo
->tags
= g_slist_copy(msginfo
->tags
);
2408 compose_extract_original_charset(compose
);
2410 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2411 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2412 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2413 gchar
*queueheader_buf
= NULL
;
2415 /* Set message save folder */
2416 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "SCF:")) {
2417 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2418 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2419 compose_set_save_to(compose
, &queueheader_buf
[4]);
2420 g_free(queueheader_buf
);
2422 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "RRCPT:")) {
2423 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2425 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2427 g_free(queueheader_buf
);
2431 if (compose_parse_header(compose
, msginfo
) < 0) {
2432 compose
->updating
= FALSE
;
2433 compose_destroy(compose
);
2435 procheader_entries_free(manual_headers
);
2438 compose_reedit_set_entry(compose
, msginfo
);
2440 textview
= GTK_TEXT_VIEW(compose
->text
);
2441 textbuf
= gtk_text_view_get_buffer(textview
);
2442 compose_create_tags(textview
, compose
);
2444 mark
= gtk_text_buffer_get_insert(textbuf
);
2445 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2447 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2448 G_CALLBACK(compose_changed_cb
),
2451 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2452 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2454 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2457 fp
= procmime_get_first_text_content(msginfo
);
2460 g_warning("can't get text part");
2464 gchar buf
[BUFFSIZE
];
2465 gboolean prev_autowrap
;
2466 GtkTextBuffer
*buffer
;
2468 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2470 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2476 compose_attach_parts(compose
, msginfo
);
2478 compose_colorize_signature(compose
);
2480 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2481 G_CALLBACK(compose_changed_cb
),
2484 if (manual_headers
!= NULL
) {
2485 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2486 procheader_entries_free(manual_headers
);
2487 compose
->updating
= FALSE
;
2488 compose_destroy(compose
);
2491 procheader_entries_free(manual_headers
);
2494 gtk_widget_grab_focus(compose
->text
);
2496 if (prefs_common
.auto_exteditor
) {
2497 compose_exec_ext_editor(compose
);
2499 compose
->modified
= FALSE
;
2500 compose_set_title(compose
);
2502 compose
->updating
= FALSE
;
2503 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2504 SCROLL_TO_CURSOR(compose
);
2506 if (compose
->deferred_destroy
) {
2507 compose_destroy(compose
);
2511 compose
->sig_str
= account_get_signature_str(compose
->account
);
2513 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2518 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2525 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2528 account
= account_get_reply_account(msginfo
,
2529 prefs_common
.reply_account_autosel
);
2530 cm_return_val_if_fail(account
!= NULL
, NULL
);
2532 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2534 compose
->updating
= TRUE
;
2536 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2537 compose
->replyinfo
= NULL
;
2538 compose
->fwdinfo
= NULL
;
2540 compose_show_first_last_header(compose
, TRUE
);
2542 gtk_widget_grab_focus(compose
->header_last
->entry
);
2544 filename
= procmsg_get_message_file(msginfo
);
2546 if (filename
== NULL
) {
2547 compose
->updating
= FALSE
;
2548 compose_destroy(compose
);
2553 compose
->redirect_filename
= filename
;
2555 /* Set save folder */
2556 item
= msginfo
->folder
;
2557 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2558 gchar
*folderidentifier
;
2560 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2561 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2562 folderidentifier
= folder_item_get_identifier(item
);
2563 compose_set_save_to(compose
, folderidentifier
);
2564 g_free(folderidentifier
);
2567 compose_attach_parts(compose
, msginfo
);
2569 if (msginfo
->subject
)
2570 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2572 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2574 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2575 _("The body of the \"Redirect\" template has an error at line %d."));
2576 quote_fmt_reset_vartable();
2577 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2579 compose_colorize_signature(compose
);
2582 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2583 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2584 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2586 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2587 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2588 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2589 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2590 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2592 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2593 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2596 if (compose
->toolbar
->draft_btn
)
2597 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2598 if (compose
->toolbar
->insert_btn
)
2599 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2600 if (compose
->toolbar
->attach_btn
)
2601 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2602 if (compose
->toolbar
->sig_btn
)
2603 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2604 if (compose
->toolbar
->exteditor_btn
)
2605 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2606 if (compose
->toolbar
->linewrap_current_btn
)
2607 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2608 if (compose
->toolbar
->linewrap_all_btn
)
2609 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2610 if (compose
->toolbar
->privacy_sign_btn
)
2611 gtk_widget_set_sensitive(compose
->toolbar
->privacy_sign_btn
, FALSE
);
2612 if (compose
->toolbar
->privacy_encrypt_btn
)
2613 gtk_widget_set_sensitive(compose
->toolbar
->privacy_encrypt_btn
, FALSE
);
2615 compose
->modified
= FALSE
;
2616 compose_set_title(compose
);
2617 compose
->updating
= FALSE
;
2618 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2619 SCROLL_TO_CURSOR(compose
);
2621 if (compose
->deferred_destroy
) {
2622 compose_destroy(compose
);
2626 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2631 const GList
*compose_get_compose_list(void)
2633 return compose_list
;
2636 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2637 ComposeEntryType type
, ComposePrefType pref_type
)
2639 const gchar
*header
;
2641 gboolean in_quote
= FALSE
;
2642 if (!address
|| *address
== '\0') return;
2649 header
= N_("Bcc:");
2651 case COMPOSE_REPLYTO
:
2652 header
= N_("Reply-To:");
2654 case COMPOSE_NEWSGROUPS
:
2655 header
= N_("Newsgroups:");
2657 case COMPOSE_FOLLOWUPTO
:
2658 header
= N_( "Followup-To:");
2660 case COMPOSE_INREPLYTO
:
2661 header
= N_( "In-Reply-To:");
2668 header
= prefs_common_translated_header_name(header
);
2670 cur
= begin
= (gchar
*)address
;
2672 /* we separate the line by commas, but not if we're inside a quoted
2674 while (*cur
!= '\0') {
2676 in_quote
= !in_quote
;
2677 if (*cur
== ',' && !in_quote
) {
2678 gchar
*tmp
= g_strdup(begin
);
2680 tmp
[cur
-begin
]='\0';
2683 while (*tmp
== ' ' || *tmp
== '\t')
2685 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2686 compose_entry_indicate(compose
, tmp
);
2693 gchar
*tmp
= g_strdup(begin
);
2695 tmp
[cur
-begin
]='\0';
2696 while (*tmp
== ' ' || *tmp
== '\t')
2698 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2699 compose_entry_indicate(compose
, tmp
);
2704 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2710 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2711 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2712 if (gtk_entry_get_text(entry
) &&
2713 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2714 /* Modify background color */
2715 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor
, color
);
2716 gtk_widget_modify_base(
2717 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2718 GTK_STATE_NORMAL
, &color
);
2720 /* Modify foreground color */
2721 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color
, color
);
2722 gtk_widget_modify_text(
2723 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2724 GTK_STATE_NORMAL
, &color
);
2729 void compose_toolbar_cb(gint action
, gpointer data
)
2731 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2732 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2734 cm_return_if_fail(compose
!= NULL
);
2738 compose_send_cb(NULL
, compose
);
2741 compose_send_later_cb(NULL
, compose
);
2744 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2747 compose_insert_file_cb(NULL
, compose
);
2750 compose_attach_cb(NULL
, compose
);
2753 compose_insert_sig(compose
, FALSE
);
2756 compose_insert_sig(compose
, TRUE
);
2759 compose_ext_editor_cb(NULL
, compose
);
2761 case A_LINEWRAP_CURRENT
:
2762 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2764 case A_LINEWRAP_ALL
:
2765 compose_wrap_all_full(compose
, TRUE
);
2768 compose_address_cb(NULL
, compose
);
2771 case A_CHECK_SPELLING
:
2772 compose_check_all(NULL
, compose
);
2775 case A_PRIVACY_SIGN
:
2777 case A_PRIVACY_ENCRYPT
:
2784 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2789 gchar
*subject
= NULL
;
2793 gchar
**attach
= NULL
;
2794 gchar
*inreplyto
= NULL
;
2795 MailField mfield
= NO_FIELD_PRESENT
;
2797 /* get mailto parts but skip from */
2798 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2801 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2802 mfield
= TO_FIELD_PRESENT
;
2805 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2807 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2809 if (!g_utf8_validate (subject
, -1, NULL
)) {
2810 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2811 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2814 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2816 mfield
= SUBJECT_FIELD_PRESENT
;
2819 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2820 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2823 gboolean prev_autowrap
= compose
->autowrap
;
2825 compose
->autowrap
= FALSE
;
2827 mark
= gtk_text_buffer_get_insert(buffer
);
2828 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2830 if (!g_utf8_validate (body
, -1, NULL
)) {
2831 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2832 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2835 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2837 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2839 compose
->autowrap
= prev_autowrap
;
2840 if (compose
->autowrap
)
2841 compose_wrap_all(compose
);
2842 mfield
= BODY_FIELD_PRESENT
;
2846 gint i
= 0, att
= 0;
2847 gchar
*warn_files
= NULL
;
2848 while (attach
[i
] != NULL
) {
2849 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2850 if (utf8_filename
) {
2851 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2852 gchar
*tmp
= g_strdup_printf("%s%s\n",
2853 warn_files
?warn_files
:"",
2859 g_free(utf8_filename
);
2861 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2866 alertpanel_notice(ngettext(
2867 "The following file has been attached: \n%s",
2868 "The following files have been attached: \n%s", att
), warn_files
);
2873 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2886 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2888 static HeaderEntry hentry
[] = {
2889 {"Reply-To:", NULL
, TRUE
},
2890 {"Cc:", NULL
, TRUE
},
2891 {"References:", NULL
, FALSE
},
2892 {"Bcc:", NULL
, TRUE
},
2893 {"Newsgroups:", NULL
, TRUE
},
2894 {"Followup-To:", NULL
, TRUE
},
2895 {"List-Post:", NULL
, FALSE
},
2896 {"X-Priority:", NULL
, FALSE
},
2897 {NULL
, NULL
, FALSE
}
2914 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2916 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2917 procheader_get_header_fields(fp
, hentry
);
2920 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2921 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2923 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2926 g_free(hentry
[H_REPLY_TO
].body
);
2927 hentry
[H_REPLY_TO
].body
= NULL
;
2929 if (hentry
[H_CC
].body
!= NULL
) {
2930 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2931 g_free(hentry
[H_CC
].body
);
2932 hentry
[H_CC
].body
= NULL
;
2934 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2935 if (compose
->mode
== COMPOSE_REEDIT
)
2936 compose
->references
= hentry
[H_REFERENCES
].body
;
2938 compose
->references
= compose_parse_references
2939 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2940 g_free(hentry
[H_REFERENCES
].body
);
2942 hentry
[H_REFERENCES
].body
= NULL
;
2944 if (hentry
[H_BCC
].body
!= NULL
) {
2945 if (compose
->mode
== COMPOSE_REEDIT
)
2947 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2948 g_free(hentry
[H_BCC
].body
);
2949 hentry
[H_BCC
].body
= NULL
;
2951 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2952 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2953 hentry
[H_NEWSGROUPS
].body
= NULL
;
2955 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2956 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2957 compose
->followup_to
=
2958 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2961 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2962 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2964 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2965 gchar
*to
= NULL
, *start
= NULL
;
2967 extract_address(hentry
[H_LIST_POST
].body
);
2968 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2969 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2971 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2972 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2975 g_free(compose
->ml_post
);
2976 compose
->ml_post
= to
;
2979 g_free(hentry
[H_LIST_POST
].body
);
2980 hentry
[H_LIST_POST
].body
= NULL
;
2983 /* CLAWS - X-Priority */
2984 if (compose
->mode
== COMPOSE_REEDIT
)
2985 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2988 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2989 g_free(hentry
[H_X_PRIORITY
].body
);
2991 hentry
[H_X_PRIORITY
].body
= NULL
;
2993 if (priority
< PRIORITY_HIGHEST
||
2994 priority
> PRIORITY_LOWEST
)
2995 priority
= PRIORITY_NORMAL
;
2997 compose
->priority
= priority
;
3000 if (compose
->mode
== COMPOSE_REEDIT
) {
3001 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3002 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
3004 if (msginfo
->msgid
&& *msginfo
->msgid
&&
3005 compose
->folder
!= NULL
&&
3006 compose
->folder
->stype
== F_DRAFT
)
3007 compose
->msgid
= g_strdup(msginfo
->msgid
);
3009 if (msginfo
->msgid
&& *msginfo
->msgid
)
3010 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
3012 if (!compose
->references
) {
3013 if (msginfo
->msgid
&& *msginfo
->msgid
) {
3014 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3015 compose
->references
=
3016 g_strdup_printf("<%s>\n\t<%s>",
3020 compose
->references
=
3021 g_strconcat("<", msginfo
->msgid
, ">",
3023 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3024 compose
->references
=
3025 g_strconcat("<", msginfo
->inreplyto
, ">",
3034 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3039 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3041 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3042 procheader_get_header_fields(fp
, entries
);
3046 while (he
!= NULL
&& he
->name
!= NULL
) {
3048 GtkListStore
*model
= NULL
;
3050 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3051 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3052 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3053 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3054 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3061 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3063 GSList
*ref_id_list
, *cur
;
3067 ref_id_list
= references_list_append(NULL
, ref
);
3068 if (!ref_id_list
) return NULL
;
3069 if (msgid
&& *msgid
)
3070 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3075 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3076 /* "<" + Message-ID + ">" + CR+LF+TAB */
3077 len
+= strlen((gchar
*)cur
->data
) + 5;
3079 if (len
> MAX_REFERENCES_LEN
) {
3080 /* remove second message-ID */
3081 if (ref_id_list
&& ref_id_list
->next
&&
3082 ref_id_list
->next
->next
) {
3083 g_free(ref_id_list
->next
->data
);
3084 ref_id_list
= g_slist_remove
3085 (ref_id_list
, ref_id_list
->next
->data
);
3087 slist_free_strings_full(ref_id_list
);
3094 new_ref
= g_string_new("");
3095 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3096 if (new_ref
->len
> 0)
3097 g_string_append(new_ref
, "\n\t");
3098 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3101 slist_free_strings_full(ref_id_list
);
3103 new_ref_str
= new_ref
->str
;
3104 g_string_free(new_ref
, FALSE
);
3109 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3110 const gchar
*fmt
, const gchar
*qmark
,
3111 const gchar
*body
, gboolean rewrap
,
3112 gboolean need_unescape
,
3113 const gchar
*err_msg
)
3115 MsgInfo
* dummyinfo
= NULL
;
3116 gchar
*quote_str
= NULL
;
3118 gboolean prev_autowrap
;
3119 const gchar
*trimmed_body
= body
;
3120 gint cursor_pos
= -1;
3121 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3122 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3127 SIGNAL_BLOCK(buffer
);
3130 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3131 msginfo
= dummyinfo
;
3134 if (qmark
!= NULL
) {
3136 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3137 compose
->gtkaspell
);
3139 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3141 quote_fmt_scan_string(qmark
);
3144 buf
= quote_fmt_get_buffer();
3147 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3149 Xstrdup_a(quote_str
, buf
, goto error
)
3152 if (fmt
&& *fmt
!= '\0') {
3155 while (*trimmed_body
== '\n')
3159 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3160 compose
->gtkaspell
);
3162 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3164 if (need_unescape
) {
3167 /* decode \-escape sequences in the internal representation of the quote format */
3168 tmp
= g_malloc(strlen(fmt
)+1);
3169 pref_get_unescaped_pref(tmp
, fmt
);
3170 quote_fmt_scan_string(tmp
);
3174 quote_fmt_scan_string(fmt
);
3178 buf
= quote_fmt_get_buffer();
3181 gint line
= quote_fmt_get_line();
3182 alertpanel_error(err_msg
, line
);
3190 prev_autowrap
= compose
->autowrap
;
3191 compose
->autowrap
= FALSE
;
3193 mark
= gtk_text_buffer_get_insert(buffer
);
3194 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3195 if (g_utf8_validate(buf
, -1, NULL
)) {
3196 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3198 gchar
*tmpout
= NULL
;
3199 tmpout
= conv_codeset_strdup
3200 (buf
, conv_get_locale_charset_str_no_utf8(),
3202 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3204 tmpout
= g_malloc(strlen(buf
)*2+1);
3205 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3207 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3211 cursor_pos
= quote_fmt_get_cursor_pos();
3212 if (cursor_pos
== -1)
3213 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3214 compose
->set_cursor_pos
= cursor_pos
;
3216 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3217 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3218 gtk_text_buffer_place_cursor(buffer
, &iter
);
3220 compose
->autowrap
= prev_autowrap
;
3221 if (compose
->autowrap
&& rewrap
)
3222 compose_wrap_all(compose
);
3229 SIGNAL_UNBLOCK(buffer
);
3231 procmsg_msginfo_free( &dummyinfo
);
3236 /* if ml_post is of type addr@host and from is of type
3237 * addr-anything@host, return TRUE
3239 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3241 gchar
*left_ml
= NULL
;
3242 gchar
*right_ml
= NULL
;
3243 gchar
*left_from
= NULL
;
3244 gchar
*right_from
= NULL
;
3245 gboolean result
= FALSE
;
3247 if (!ml_post
|| !from
)
3250 left_ml
= g_strdup(ml_post
);
3251 if (strstr(left_ml
, "@")) {
3252 right_ml
= strstr(left_ml
, "@")+1;
3253 *(strstr(left_ml
, "@")) = '\0';
3256 left_from
= g_strdup(from
);
3257 if (strstr(left_from
, "@")) {
3258 right_from
= strstr(left_from
, "@")+1;
3259 *(strstr(left_from
, "@")) = '\0';
3262 if (right_ml
&& right_from
3263 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3264 && !strcmp(right_from
, right_ml
)) {
3273 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3274 gboolean respect_default_to
)
3278 if (!folder
|| !folder
->prefs
)
3281 if (folder
->prefs
->enable_default_from
) {
3282 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), folder
->prefs
->default_from
);
3283 compose_entry_indicate(compose
, folder
->prefs
->default_from
);
3285 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3286 compose_entry_append(compose
, folder
->prefs
->default_to
,
3287 COMPOSE_TO
, PREF_FOLDER
);
3288 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3290 if (folder
->prefs
->enable_default_cc
) {
3291 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3292 COMPOSE_CC
, PREF_FOLDER
);
3293 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3295 if (folder
->prefs
->enable_default_bcc
) {
3296 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3297 COMPOSE_BCC
, PREF_FOLDER
);
3298 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3300 if (folder
->prefs
->enable_default_replyto
) {
3301 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3302 COMPOSE_REPLYTO
, PREF_FOLDER
);
3303 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3307 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3312 if (!compose
|| !msginfo
)
3315 if (msginfo
->subject
&& *msginfo
->subject
) {
3316 buf
= p
= g_strdup(msginfo
->subject
);
3317 p
+= subject_get_prefix_length(p
);
3318 memmove(buf
, p
, strlen(p
) + 1);
3320 buf2
= g_strdup_printf("Re: %s", buf
);
3321 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3326 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3329 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3330 gboolean to_all
, gboolean to_ml
,
3332 gboolean followup_and_reply_to
)
3334 GSList
*cc_list
= NULL
;
3337 gchar
*replyto
= NULL
;
3338 gchar
*ac_email
= NULL
;
3340 gboolean reply_to_ml
= FALSE
;
3341 gboolean default_reply_to
= FALSE
;
3343 cm_return_if_fail(compose
->account
!= NULL
);
3344 cm_return_if_fail(msginfo
!= NULL
);
3346 reply_to_ml
= to_ml
&& compose
->ml_post
;
3348 default_reply_to
= msginfo
->folder
&&
3349 msginfo
->folder
->prefs
->enable_default_reply_to
;
3351 if (compose
->account
->protocol
!= A_NNTP
) {
3352 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3354 if (reply_to_ml
&& !default_reply_to
) {
3356 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3359 /* normal answer to ml post with a reply-to */
3360 compose_entry_append(compose
,
3362 COMPOSE_TO
, PREF_ML
);
3363 if (compose
->replyto
)
3364 compose_entry_append(compose
,
3366 COMPOSE_CC
, PREF_ML
);
3368 /* answer to subscription confirmation */
3369 if (compose
->replyto
)
3370 compose_entry_append(compose
,
3372 COMPOSE_TO
, PREF_ML
);
3373 else if (msginfo
->from
)
3374 compose_entry_append(compose
,
3376 COMPOSE_TO
, PREF_ML
);
3379 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3380 compose_entry_append(compose
,
3381 msginfo
->folder
->prefs
->default_reply_to
,
3382 COMPOSE_TO
, PREF_FOLDER
);
3383 compose_entry_indicate(compose
,
3384 msginfo
->folder
->prefs
->default_reply_to
);
3390 compose_entry_append(compose
, msginfo
->from
,
3391 COMPOSE_TO
, PREF_NONE
);
3393 Xstrdup_a(tmp1
, msginfo
->from
, return);
3394 extract_address(tmp1
);
3395 compose_entry_append(compose
,
3396 (!account_find_from_address(tmp1
, FALSE
))
3399 COMPOSE_TO
, PREF_NONE
);
3400 if (compose
->replyto
)
3401 compose_entry_append(compose
,
3403 COMPOSE_CC
, PREF_NONE
);
3405 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3406 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3407 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3408 if (compose
->replyto
) {
3409 compose_entry_append(compose
,
3411 COMPOSE_TO
, PREF_NONE
);
3413 compose_entry_append(compose
,
3414 msginfo
->from
? msginfo
->from
: "",
3415 COMPOSE_TO
, PREF_NONE
);
3418 /* replying to own mail, use original recp */
3419 compose_entry_append(compose
,
3420 msginfo
->to
? msginfo
->to
: "",
3421 COMPOSE_TO
, PREF_NONE
);
3422 compose_entry_append(compose
,
3423 msginfo
->cc
? msginfo
->cc
: "",
3424 COMPOSE_CC
, PREF_NONE
);
3429 if (to_sender
|| (compose
->followup_to
&&
3430 !strncmp(compose
->followup_to
, "poster", 6)))
3431 compose_entry_append
3433 (compose
->replyto
? compose
->replyto
:
3434 msginfo
->from
? msginfo
->from
: ""),
3435 COMPOSE_TO
, PREF_NONE
);
3437 else if (followup_and_reply_to
|| to_all
) {
3438 compose_entry_append
3440 (compose
->replyto
? compose
->replyto
:
3441 msginfo
->from
? msginfo
->from
: ""),
3442 COMPOSE_TO
, PREF_NONE
);
3444 compose_entry_append
3446 compose
->followup_to
? compose
->followup_to
:
3447 compose
->newsgroups
? compose
->newsgroups
: "",
3448 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3450 compose_entry_append
3452 msginfo
->cc
? msginfo
->cc
: "",
3453 COMPOSE_CC
, PREF_NONE
);
3456 compose_entry_append
3458 compose
->followup_to
? compose
->followup_to
:
3459 compose
->newsgroups
? compose
->newsgroups
: "",
3460 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3462 compose_reply_set_subject(compose
, msginfo
);
3464 if (to_ml
&& compose
->ml_post
) return;
3465 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3467 if (compose
->replyto
) {
3468 Xstrdup_a(replyto
, compose
->replyto
, return);
3469 extract_address(replyto
);
3471 if (msginfo
->from
) {
3472 Xstrdup_a(from
, msginfo
->from
, return);
3473 extract_address(from
);
3476 if (replyto
&& from
)
3477 cc_list
= address_list_append_with_comments(cc_list
, from
);
3478 if (to_all
&& msginfo
->folder
&&
3479 msginfo
->folder
->prefs
->enable_default_reply_to
)
3480 cc_list
= address_list_append_with_comments(cc_list
,
3481 msginfo
->folder
->prefs
->default_reply_to
);
3482 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3483 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3485 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3488 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3489 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3490 extract_address(addr
);
3492 if (strcmp(ac_email
, addr
))
3493 compose_entry_append(compose
, (gchar
*)cur
->data
,
3494 COMPOSE_CC
, PREF_NONE
);
3496 debug_print("Cc address same as compose account's, ignoring\n");
3501 slist_free_strings_full(cc_list
);
3507 #define SET_ENTRY(entry, str) \
3510 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3513 #define SET_ADDRESS(type, str) \
3516 compose_entry_append(compose, str, type, PREF_NONE); \
3519 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3521 cm_return_if_fail(msginfo
!= NULL
);
3523 SET_ENTRY(subject_entry
, msginfo
->subject
);
3524 SET_ENTRY(from_name
, msginfo
->from
);
3525 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3526 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3527 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3528 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3529 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3530 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3532 compose_update_priority_menu_item(compose
);
3533 compose_update_privacy_system_menu_item(compose
, FALSE
);
3534 compose_show_first_last_header(compose
, TRUE
);
3540 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3542 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3543 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3545 GtkTextIter iter
, iter_end
;
3546 gint cur_pos
, ins_pos
;
3547 gboolean prev_autowrap
;
3548 gboolean found
= FALSE
;
3549 gboolean exists
= FALSE
;
3551 cm_return_if_fail(compose
->account
!= NULL
);
3555 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3556 G_CALLBACK(compose_changed_cb
),
3559 mark
= gtk_text_buffer_get_insert(buffer
);
3560 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3561 cur_pos
= gtk_text_iter_get_offset (&iter
);
3564 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3566 exists
= (compose
->sig_str
!= NULL
);
3569 GtkTextIter first_iter
, start_iter
, end_iter
;
3571 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3573 if (!exists
|| compose
->sig_str
[0] == '\0')
3576 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3577 compose
->signature_tag
);
3580 /* include previous \n\n */
3581 gtk_text_iter_backward_chars(&first_iter
, 1);
3582 start_iter
= first_iter
;
3583 end_iter
= first_iter
;
3585 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3586 compose
->signature_tag
);
3587 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3588 compose
->signature_tag
);
3590 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3596 g_free(compose
->sig_str
);
3597 compose
->sig_str
= account_get_signature_str(compose
->account
);
3599 cur_pos
= gtk_text_iter_get_offset(&iter
);
3601 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3602 g_free(compose
->sig_str
);
3603 compose
->sig_str
= NULL
;
3605 if (compose
->sig_inserted
== FALSE
)
3606 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3607 compose
->sig_inserted
= TRUE
;
3609 cur_pos
= gtk_text_iter_get_offset(&iter
);
3610 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3612 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3613 gtk_text_iter_forward_chars(&iter
, 1);
3614 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3615 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3617 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3618 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3621 /* put the cursor where it should be
3622 * either where the quote_fmt says, either where it was */
3623 if (compose
->set_cursor_pos
< 0)
3624 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3626 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3627 compose
->set_cursor_pos
);
3629 compose
->set_cursor_pos
= -1;
3630 gtk_text_buffer_place_cursor(buffer
, &iter
);
3631 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3632 G_CALLBACK(compose_changed_cb
),
3638 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3641 GtkTextBuffer
*buffer
;
3644 const gchar
*cur_encoding
;
3645 gchar buf
[BUFFSIZE
];
3648 gboolean prev_autowrap
;
3652 GError
*error
= NULL
;
3658 GString
*file_contents
= NULL
;
3659 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3661 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3663 /* get the size of the file we are about to insert */
3665 f
= g_file_new_for_path(file
);
3666 fi
= g_file_query_info(f
, "standard::size",
3667 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3669 if (error
!= NULL
) {
3670 g_warning(error
->message
);
3672 g_error_free(error
);
3676 ret
= g_stat(file
, &file_stat
);
3679 gchar
*shortfile
= g_path_get_basename(file
);
3680 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3682 return COMPOSE_INSERT_NO_FILE
;
3683 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3685 size
= g_file_info_get_size(fi
);
3689 size
= file_stat
.st_size
;
3692 /* ask user for confirmation if the file is large */
3693 if (prefs_common
.warn_large_insert_size
< 0 ||
3694 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3698 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3699 "in the message body. Are you sure you want to do that?"),
3700 to_human_readable(size
));
3701 aval
= alertpanel_full(_("Are you sure?"), msg
, NULL
, _("_Cancel"),
3702 NULL
, _("_Insert"), NULL
, NULL
, ALERTFOCUS_SECOND
, TRUE
,
3703 NULL
, ALERT_QUESTION
);
3706 /* do we ask for confirmation next time? */
3707 if (aval
& G_ALERTDISABLE
) {
3708 /* no confirmation next time, disable feature in preferences */
3709 aval
&= ~G_ALERTDISABLE
;
3710 prefs_common
.warn_large_insert
= FALSE
;
3713 /* abort file insertion if user canceled action */
3714 if (aval
!= G_ALERTALTERNATE
) {
3715 return COMPOSE_INSERT_NO_FILE
;
3721 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3722 FILE_OP_ERROR(file
, "claws_fopen");
3723 return COMPOSE_INSERT_READ_ERROR
;
3726 prev_autowrap
= compose
->autowrap
;
3727 compose
->autowrap
= FALSE
;
3729 text
= GTK_TEXT_VIEW(compose
->text
);
3730 buffer
= gtk_text_view_get_buffer(text
);
3731 mark
= gtk_text_buffer_get_insert(buffer
);
3732 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3734 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3735 G_CALLBACK(text_inserted
),
3738 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3740 file_contents
= g_string_new("");
3741 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3744 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3745 str
= g_strdup(buf
);
3747 codeconv_set_strict(TRUE
);
3748 str
= conv_codeset_strdup
3749 (buf
, cur_encoding
, CS_INTERNAL
);
3750 codeconv_set_strict(FALSE
);
3753 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3759 /* strip <CR> if DOS/Windows file,
3760 replace <CR> with <LF> if Macintosh file. */
3763 if (len
> 0 && str
[len
- 1] != '\n') {
3765 if (str
[len
] == '\r') str
[len
] = '\n';
3768 file_contents
= g_string_append(file_contents
, str
);
3772 if (result
== COMPOSE_INSERT_SUCCESS
) {
3773 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3775 compose_changed_cb(NULL
, compose
);
3776 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3777 G_CALLBACK(text_inserted
),
3779 compose
->autowrap
= prev_autowrap
;
3780 if (compose
->autowrap
)
3781 compose_wrap_all(compose
);
3784 g_string_free(file_contents
, TRUE
);
3790 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3791 const gchar
*filename
,
3792 const gchar
*content_type
,
3793 const gchar
*charset
)
3801 GtkListStore
*store
;
3803 gboolean has_binary
= FALSE
;
3805 if (!is_file_exist(file
)) {
3806 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3807 gboolean result
= FALSE
;
3808 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3809 result
= compose_attach_append(
3810 compose
, file_from_uri
,
3811 filename
, content_type
,
3814 g_free(file_from_uri
);
3817 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3820 if ((size
= get_file_size(file
)) < 0) {
3821 alertpanel_error("Can't get file size of %s\n", filename
);
3825 /* In batch mode, we allow 0-length files to be attached no questions asked */
3826 if (size
== 0 && !compose
->batch
) {
3827 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3828 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3829 NULL
, _("_Cancel"), NULL
, _("_Attach anyway"),
3830 NULL
, NULL
, ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3833 if (aval
!= G_ALERTALTERNATE
) {
3837 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3838 alertpanel_error(_("Can't read %s."), filename
);
3843 ainfo
= g_new0(AttachInfo
, 1);
3844 auto_ainfo
= g_auto_pointer_new_with_free
3845 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3846 ainfo
->file
= g_strdup(file
);
3849 ainfo
->content_type
= g_strdup(content_type
);
3850 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3852 MsgFlags flags
= {0, 0};
3854 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3855 ainfo
->encoding
= ENC_7BIT
;
3857 ainfo
->encoding
= ENC_8BIT
;
3859 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3860 if (msginfo
&& msginfo
->subject
)
3861 name
= g_strdup(msginfo
->subject
);
3863 name
= g_path_get_basename(filename
? filename
: file
);
3865 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3867 procmsg_msginfo_free(&msginfo
);
3869 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3870 ainfo
->charset
= g_strdup(charset
);
3871 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3873 ainfo
->encoding
= ENC_BASE64
;
3875 name
= g_path_get_basename(filename
? filename
: file
);
3876 ainfo
->name
= g_strdup(name
);
3880 ainfo
->content_type
= procmime_get_mime_type(file
);
3881 if (!ainfo
->content_type
) {
3882 ainfo
->content_type
=
3883 g_strdup("application/octet-stream");
3884 ainfo
->encoding
= ENC_BASE64
;
3885 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3887 procmime_get_encoding_for_text_file(file
, &has_binary
);
3889 ainfo
->encoding
= ENC_BASE64
;
3890 name
= g_path_get_basename(filename
? filename
: file
);
3891 ainfo
->name
= g_strdup(name
);
3895 if (ainfo
->name
!= NULL
3896 && !strcmp(ainfo
->name
, ".")) {
3897 g_free(ainfo
->name
);
3901 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3902 g_free(ainfo
->content_type
);
3903 ainfo
->content_type
= g_strdup("application/octet-stream");
3904 g_free(ainfo
->charset
);
3905 ainfo
->charset
= NULL
;
3908 ainfo
->size
= (goffset
)size
;
3909 size_text
= to_human_readable((goffset
)size
);
3911 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3912 (GTK_TREE_VIEW(compose
->attach_clist
)));
3914 gtk_list_store_append(store
, &iter
);
3915 gtk_list_store_set(store
, &iter
,
3916 COL_MIMETYPE
, ainfo
->content_type
,
3917 COL_SIZE
, size_text
,
3918 COL_NAME
, ainfo
->name
,
3919 COL_CHARSET
, ainfo
->charset
,
3921 COL_AUTODATA
, auto_ainfo
,
3924 g_auto_pointer_free(auto_ainfo
);
3925 compose_attach_update_label(compose
);
3929 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3931 compose
->use_signing
= use_signing
;
3932 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3935 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3937 compose
->use_encryption
= use_encryption
;
3938 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3941 #define NEXT_PART_NOT_CHILD(info) \
3943 node = info->node; \
3944 while (node->children) \
3945 node = g_node_last_child(node); \
3946 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3949 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3953 MimeInfo
*firsttext
= NULL
;
3954 MimeInfo
*encrypted
= NULL
;
3957 const gchar
*partname
= NULL
;
3959 mimeinfo
= procmime_scan_message(msginfo
);
3960 if (!mimeinfo
) return;
3962 if (mimeinfo
->node
->children
== NULL
) {
3963 procmime_mimeinfo_free_all(&mimeinfo
);
3967 /* find first content part */
3968 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3969 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3970 child
= (MimeInfo
*)child
->node
->children
->data
;
3973 if (child
->type
== MIMETYPE_TEXT
) {
3975 debug_print("First text part found\n");
3976 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3977 child
->type
== MIMETYPE_APPLICATION
&&
3978 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3979 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3982 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3983 while (child
!= NULL
) {
3986 if (child
== encrypted
) {
3987 /* skip this part of tree */
3988 NEXT_PART_NOT_CHILD(child
);
3992 if (child
->type
== MIMETYPE_MULTIPART
) {
3993 /* get the actual content */
3994 child
= procmime_mimeinfo_next(child
);
3998 if (child
== firsttext
) {
3999 child
= procmime_mimeinfo_next(child
);
4003 outfile
= procmime_get_tmp_file_name(child
);
4004 if ((err
= procmime_get_part(outfile
, child
)) < 0)
4005 g_warning("can't get the part of multipart message. (%s)", g_strerror(-err
));
4007 gchar
*content_type
;
4009 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
4011 /* if we meet a pgp signature, we don't attach it, but
4012 * we force signing. */
4013 if ((strcmp(content_type
, "application/pgp-signature") &&
4014 strcmp(content_type
, "application/pkcs7-signature") &&
4015 strcmp(content_type
, "application/x-pkcs7-signature"))
4016 || compose
->mode
== COMPOSE_REDIRECT
) {
4017 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
4018 if (partname
== NULL
)
4019 partname
= procmime_mimeinfo_get_parameter(child
, "name");
4020 if (partname
== NULL
)
4022 compose_attach_append(compose
, outfile
,
4023 partname
, content_type
,
4024 procmime_mimeinfo_get_parameter(child
, "charset"));
4026 compose_force_signing(compose
, compose
->account
, NULL
);
4028 g_free(content_type
);
4031 NEXT_PART_NOT_CHILD(child
);
4033 procmime_mimeinfo_free_all(&mimeinfo
);
4036 #undef NEXT_PART_NOT_CHILD
4041 WAIT_FOR_INDENT_CHAR
,
4042 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4045 /* return indent length, we allow:
4046 indent characters followed by indent characters or spaces/tabs,
4047 alphabets and numbers immediately followed by indent characters,
4048 and the repeating sequences of the above
4049 If quote ends with multiple spaces, only the first one is included. */
4050 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4051 const GtkTextIter
*start
, gint
*len
)
4053 GtkTextIter iter
= *start
;
4057 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4060 gint alnum_count
= 0;
4061 gint space_count
= 0;
4064 if (prefs_common
.quote_chars
== NULL
) {
4068 while (!gtk_text_iter_ends_line(&iter
)) {
4069 wc
= gtk_text_iter_get_char(&iter
);
4070 if (g_unichar_iswide(wc
))
4072 clen
= g_unichar_to_utf8(wc
, ch
);
4076 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4077 is_space
= g_unichar_isspace(wc
);
4079 if (state
== WAIT_FOR_INDENT_CHAR
) {
4080 if (!is_indent
&& !g_unichar_isalnum(wc
))
4083 quote_len
+= alnum_count
+ space_count
+ 1;
4084 alnum_count
= space_count
= 0;
4085 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4088 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4089 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4093 else if (is_indent
) {
4094 quote_len
+= alnum_count
+ space_count
+ 1;
4095 alnum_count
= space_count
= 0;
4098 state
= WAIT_FOR_INDENT_CHAR
;
4102 gtk_text_iter_forward_char(&iter
);
4105 if (quote_len
> 0 && space_count
> 0)
4111 if (quote_len
> 0) {
4113 gtk_text_iter_forward_chars(&iter
, quote_len
);
4114 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4120 /* return >0 if the line is itemized */
4121 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4122 const GtkTextIter
*start
)
4124 GtkTextIter iter
= *start
;
4129 if (gtk_text_iter_ends_line(&iter
))
4134 wc
= gtk_text_iter_get_char(&iter
);
4135 if (!g_unichar_isspace(wc
))
4137 gtk_text_iter_forward_char(&iter
);
4138 if (gtk_text_iter_ends_line(&iter
))
4142 clen
= g_unichar_to_utf8(wc
, ch
);
4143 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4145 wc
== 0x2022 || /* BULLET */
4146 wc
== 0x2023 || /* TRIANGULAR BULLET */
4147 wc
== 0x2043 || /* HYPHEN BULLET */
4148 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4149 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4150 wc
== 0x2219 || /* BULLET OPERATOR */
4151 wc
== 0x25d8 || /* INVERSE BULLET */
4152 wc
== 0x25e6 || /* WHITE BULLET */
4153 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4154 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4155 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4156 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4157 wc
== 0x29bf /* CIRCLED BULLET */
4161 gtk_text_iter_forward_char(&iter
);
4162 if (gtk_text_iter_ends_line(&iter
))
4164 wc
= gtk_text_iter_get_char(&iter
);
4165 if (g_unichar_isspace(wc
)) {
4171 /* return the string at the start of the itemization */
4172 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4173 const GtkTextIter
*start
)
4175 GtkTextIter iter
= *start
;
4178 GString
*item_chars
= g_string_new("");
4181 if (gtk_text_iter_ends_line(&iter
)) {
4182 g_string_free(item_chars
, FALSE
);
4188 wc
= gtk_text_iter_get_char(&iter
);
4189 if (!g_unichar_isspace(wc
))
4191 gtk_text_iter_forward_char(&iter
);
4192 if (gtk_text_iter_ends_line(&iter
))
4194 g_string_append_unichar(item_chars
, wc
);
4197 str
= item_chars
->str
;
4198 g_string_free(item_chars
, FALSE
);
4202 /* return the number of spaces at a line's start */
4203 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4204 const GtkTextIter
*start
)
4206 GtkTextIter iter
= *start
;
4209 if (gtk_text_iter_ends_line(&iter
))
4213 wc
= gtk_text_iter_get_char(&iter
);
4214 if (!g_unichar_isspace(wc
))
4217 gtk_text_iter_forward_char(&iter
);
4218 if (gtk_text_iter_ends_line(&iter
))
4222 gtk_text_iter_forward_char(&iter
);
4223 if (gtk_text_iter_ends_line(&iter
))
4228 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4229 const GtkTextIter
*start
,
4230 GtkTextIter
*break_pos
,
4234 GtkTextIter iter
= *start
, line_end
= *start
;
4235 PangoLogAttr
*attrs
;
4242 gboolean can_break
= FALSE
;
4243 gboolean do_break
= FALSE
;
4244 gboolean was_white
= FALSE
;
4245 gboolean prev_dont_break
= FALSE
;
4247 gtk_text_iter_forward_to_line_end(&line_end
);
4248 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4249 len
= g_utf8_strlen(str
, -1);
4253 g_warning("compose_get_line_break_pos: len = 0!");
4257 /* g_print("breaking line: %d: %s (len = %d)\n",
4258 gtk_text_iter_get_line(&iter), str, len); */
4260 attrs
= g_new(PangoLogAttr
, len
+ 1);
4262 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4266 /* skip quote and leading spaces */
4267 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4270 wc
= g_utf8_get_char(p
);
4271 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4273 if (g_unichar_iswide(wc
))
4275 else if (*p
== '\t')
4279 p
= g_utf8_next_char(p
);
4282 for (; *p
!= '\0' && i
< len
; i
++) {
4283 PangoLogAttr
*attr
= attrs
+ i
;
4284 gunichar wc
= g_utf8_get_char(p
);
4287 /* attr->is_line_break will be false for some characters that
4288 * we want to break a line before, like '/' or ':', so we
4289 * also allow breaking on any non-wide character. The
4290 * mentioned pango attribute is still useful to decide on
4291 * line breaks when wide characters are involved. */
4292 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4293 && can_break
&& was_white
&& !prev_dont_break
)
4296 was_white
= attr
->is_white
;
4298 /* don't wrap URI */
4299 if ((uri_len
= get_uri_len(p
)) > 0) {
4301 if (pos
> 0 && col
> max_col
) {
4311 if (g_unichar_iswide(wc
)) {
4313 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4315 } else if (*p
== '\t')
4319 if (pos
> 0 && col
> max_col
) {
4324 if (*p
== '-' || *p
== '/')
4325 prev_dont_break
= TRUE
;
4327 prev_dont_break
= FALSE
;
4329 p
= g_utf8_next_char(p
);
4333 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4338 *break_pos
= *start
;
4339 gtk_text_iter_set_line_offset(break_pos
, pos
);
4344 static gboolean
compose_join_next_line(Compose
*compose
,
4345 GtkTextBuffer
*buffer
,
4347 const gchar
*quote_str
)
4349 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4350 PangoLogAttr attrs
[3];
4352 gchar
*next_quote_str
;
4355 gboolean keep_cursor
= FALSE
;
4357 if (!gtk_text_iter_forward_line(&iter_
) ||
4358 gtk_text_iter_ends_line(&iter_
)) {
4361 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4363 if ((quote_str
|| next_quote_str
) &&
4364 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4365 g_free(next_quote_str
);
4368 g_free(next_quote_str
);
4371 if (quote_len
> 0) {
4372 gtk_text_iter_forward_chars(&end
, quote_len
);
4373 if (gtk_text_iter_ends_line(&end
)) {
4378 /* don't join itemized lines */
4379 if (compose_itemized_length(buffer
, &end
) > 0) {
4383 /* don't join signature separator */
4384 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4387 /* delete quote str */
4389 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4391 /* don't join line breaks put by the user */
4393 gtk_text_iter_backward_char(&cur
);
4394 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4395 gtk_text_iter_forward_char(&cur
);
4399 gtk_text_iter_forward_char(&cur
);
4400 /* delete linebreak and extra spaces */
4401 while (gtk_text_iter_backward_char(&cur
)) {
4402 wc1
= gtk_text_iter_get_char(&cur
);
4403 if (!g_unichar_isspace(wc1
))
4408 while (!gtk_text_iter_ends_line(&cur
)) {
4409 wc1
= gtk_text_iter_get_char(&cur
);
4410 if (!g_unichar_isspace(wc1
))
4412 gtk_text_iter_forward_char(&cur
);
4415 if (!gtk_text_iter_equal(&prev
, &next
)) {
4418 mark
= gtk_text_buffer_get_insert(buffer
);
4419 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4420 if (gtk_text_iter_equal(&prev
, &cur
))
4422 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4426 /* insert space if required */
4427 gtk_text_iter_backward_char(&prev
);
4428 wc1
= gtk_text_iter_get_char(&prev
);
4429 wc2
= gtk_text_iter_get_char(&next
);
4430 gtk_text_iter_forward_char(&next
);
4431 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4432 pango_default_break(str
, -1, NULL
, attrs
, 3);
4433 if (!attrs
[1].is_line_break
||
4434 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4435 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4437 gtk_text_iter_backward_char(&iter_
);
4438 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4447 #define ADD_TXT_POS(bp_, ep_, pti_) \
4448 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4449 last = last->next; \
4450 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4451 last->next = NULL; \
4453 g_warning("alloc error scanning URIs"); \
4456 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4458 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4459 GtkTextBuffer
*buffer
;
4460 GtkTextIter iter
, break_pos
, end_of_line
;
4461 gchar
*quote_str
= NULL
;
4463 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4464 gboolean prev_autowrap
= compose
->autowrap
;
4465 gint startq_offset
= -1, noq_offset
= -1;
4466 gint uri_start
= -1, uri_stop
= -1;
4467 gint nouri_start
= -1, nouri_stop
= -1;
4468 gint num_blocks
= 0;
4469 gint quotelevel
= -1;
4470 gboolean modified
= force
;
4471 gboolean removed
= FALSE
;
4472 gboolean modified_before_remove
= FALSE
;
4474 gboolean start
= TRUE
;
4475 gint itemized_len
= 0, rem_item_len
= 0;
4476 gchar
*itemized_chars
= NULL
;
4477 gboolean item_continuation
= FALSE
;
4482 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4486 compose
->autowrap
= FALSE
;
4488 buffer
= gtk_text_view_get_buffer(text
);
4489 undo_wrapping(compose
->undostruct
, TRUE
);
4494 mark
= gtk_text_buffer_get_insert(buffer
);
4495 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4499 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4500 if (gtk_text_iter_ends_line(&iter
)) {
4501 while (gtk_text_iter_ends_line(&iter
) &&
4502 gtk_text_iter_forward_line(&iter
))
4505 while (gtk_text_iter_backward_line(&iter
)) {
4506 if (gtk_text_iter_ends_line(&iter
)) {
4507 gtk_text_iter_forward_line(&iter
);
4513 /* move to line start */
4514 gtk_text_iter_set_line_offset(&iter
, 0);
4517 itemized_len
= compose_itemized_length(buffer
, &iter
);
4519 if (!itemized_len
) {
4520 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4521 item_continuation
= TRUE
;
4525 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4527 /* go until paragraph end (empty line) */
4528 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4529 gchar
*scanpos
= NULL
;
4530 /* parse table - in order of priority */
4532 const gchar
*needle
; /* token */
4534 /* token search function */
4535 gchar
*(*search
) (const gchar
*haystack
,
4536 const gchar
*needle
);
4537 /* part parsing function */
4538 gboolean (*parse
) (const gchar
*start
,
4539 const gchar
*scanpos
,
4543 /* part to URI function */
4544 gchar
*(*build_uri
) (const gchar
*bp
,
4548 static struct table parser
[] = {
4549 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4550 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4551 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4552 {"ftps://", strcasestr
, get_uri_part
, make_uri_string
},
4553 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4554 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4555 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4556 {"webcal://",strcasestr
, get_uri_part
, make_uri_string
},
4557 {"webcals://",strcasestr
, get_uri_part
, make_uri_string
},
4558 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4559 {"@", strcasestr
, get_email_part
, make_email_string
}
4561 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4562 gint last_index
= PARSE_ELEMS
;
4564 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4568 if (!prev_autowrap
&& num_blocks
== 0) {
4570 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4571 G_CALLBACK(text_inserted
),
4574 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4577 uri_start
= uri_stop
= -1;
4579 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4582 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4583 if (startq_offset
== -1)
4584 startq_offset
= gtk_text_iter_get_offset(&iter
);
4585 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4586 if (quotelevel
> 2) {
4587 /* recycle colors */
4588 if (prefs_common
.recycle_quote_colors
)
4597 if (startq_offset
== -1)
4598 noq_offset
= gtk_text_iter_get_offset(&iter
);
4602 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4605 if (gtk_text_iter_ends_line(&iter
)) {
4607 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4608 prefs_common
.linewrap_len
,
4610 GtkTextIter prev
, next
, cur
;
4611 if (prev_autowrap
!= FALSE
|| force
) {
4612 compose
->automatic_break
= TRUE
;
4614 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4615 compose
->automatic_break
= FALSE
;
4616 if (itemized_len
&& compose
->autoindent
) {
4617 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4618 if (!item_continuation
)
4619 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4621 } else if (quote_str
&& wrap_quote
) {
4622 compose
->automatic_break
= TRUE
;
4624 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4625 compose
->automatic_break
= FALSE
;
4626 if (itemized_len
&& compose
->autoindent
) {
4627 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4628 if (!item_continuation
)
4629 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4633 /* remove trailing spaces */
4635 rem_item_len
= itemized_len
;
4636 while (compose
->autoindent
&& rem_item_len
-- > 0)
4637 gtk_text_iter_backward_char(&cur
);
4638 gtk_text_iter_backward_char(&cur
);
4641 while (!gtk_text_iter_starts_line(&cur
)) {
4644 gtk_text_iter_backward_char(&cur
);
4645 wc
= gtk_text_iter_get_char(&cur
);
4646 if (!g_unichar_isspace(wc
))
4650 if (!gtk_text_iter_equal(&prev
, &next
)) {
4651 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4653 gtk_text_iter_forward_char(&break_pos
);
4657 gtk_text_buffer_insert(buffer
, &break_pos
,
4661 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4663 /* move iter to current line start */
4664 gtk_text_iter_set_line_offset(&iter
, 0);
4671 /* move iter to next line start */
4677 if (!prev_autowrap
&& num_blocks
> 0) {
4679 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4680 G_CALLBACK(text_inserted
),
4684 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4685 gtk_text_iter_forward_char(&end_of_line
);
4687 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4689 nouri_start
= gtk_text_iter_get_offset(&iter
);
4690 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4692 walk_pos
= gtk_text_iter_get_offset(&iter
);
4693 /* FIXME: this looks phony. scanning for anything in the parse table */
4694 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4697 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4699 if (scanpos
== NULL
|| tmp
< scanpos
) {
4708 /* check if URI can be parsed */
4709 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4710 (const gchar
**)&ep
, FALSE
)
4711 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4715 strlen(parser
[last_index
].needle
);
4718 uri_start
= walk_pos
+ (bp
- o_walk
);
4719 uri_stop
= walk_pos
+ (ep
- o_walk
);
4723 gtk_text_iter_forward_line(&iter
);
4726 if (startq_offset
!= -1) {
4727 GtkTextIter startquote
, endquote
;
4728 gtk_text_buffer_get_iter_at_offset(
4729 buffer
, &startquote
, startq_offset
);
4732 switch (quotelevel
) {
4734 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4735 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4736 gtk_text_buffer_apply_tag_by_name(
4737 buffer
, "quote0", &startquote
, &endquote
);
4738 gtk_text_buffer_remove_tag_by_name(
4739 buffer
, "quote1", &startquote
, &endquote
);
4740 gtk_text_buffer_remove_tag_by_name(
4741 buffer
, "quote2", &startquote
, &endquote
);
4746 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4747 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4748 gtk_text_buffer_apply_tag_by_name(
4749 buffer
, "quote1", &startquote
, &endquote
);
4750 gtk_text_buffer_remove_tag_by_name(
4751 buffer
, "quote0", &startquote
, &endquote
);
4752 gtk_text_buffer_remove_tag_by_name(
4753 buffer
, "quote2", &startquote
, &endquote
);
4758 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4759 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4760 gtk_text_buffer_apply_tag_by_name(
4761 buffer
, "quote2", &startquote
, &endquote
);
4762 gtk_text_buffer_remove_tag_by_name(
4763 buffer
, "quote0", &startquote
, &endquote
);
4764 gtk_text_buffer_remove_tag_by_name(
4765 buffer
, "quote1", &startquote
, &endquote
);
4771 } else if (noq_offset
!= -1) {
4772 GtkTextIter startnoquote
, endnoquote
;
4773 gtk_text_buffer_get_iter_at_offset(
4774 buffer
, &startnoquote
, noq_offset
);
4777 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4778 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4779 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4780 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4781 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4782 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4783 gtk_text_buffer_remove_tag_by_name(
4784 buffer
, "quote0", &startnoquote
, &endnoquote
);
4785 gtk_text_buffer_remove_tag_by_name(
4786 buffer
, "quote1", &startnoquote
, &endnoquote
);
4787 gtk_text_buffer_remove_tag_by_name(
4788 buffer
, "quote2", &startnoquote
, &endnoquote
);
4794 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4795 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4796 gtk_text_buffer_get_iter_at_offset(
4797 buffer
, &nouri_start_iter
, nouri_start
);
4798 gtk_text_buffer_get_iter_at_offset(
4799 buffer
, &nouri_end_iter
, nouri_stop
);
4800 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4801 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4802 gtk_text_buffer_remove_tag_by_name(
4803 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4804 modified_before_remove
= modified
;
4809 if (uri_start
>= 0 && uri_stop
> 0) {
4810 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4811 gtk_text_buffer_get_iter_at_offset(
4812 buffer
, &uri_start_iter
, uri_start
);
4813 gtk_text_buffer_get_iter_at_offset(
4814 buffer
, &uri_end_iter
, uri_stop
);
4815 back
= uri_end_iter
;
4816 gtk_text_iter_backward_char(&back
);
4817 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4818 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4819 gtk_text_buffer_apply_tag_by_name(
4820 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4822 if (removed
&& !modified_before_remove
) {
4828 /* debug_print("not modified, out after %d lines\n", lines); */
4832 /* debug_print("modified, out after %d lines\n", lines); */
4834 g_free(itemized_chars
);
4837 undo_wrapping(compose
->undostruct
, FALSE
);
4838 compose
->autowrap
= prev_autowrap
;
4843 void compose_action_cb(void *data
)
4845 Compose
*compose
= (Compose
*)data
;
4846 compose_wrap_all(compose
);
4849 static void compose_wrap_all(Compose
*compose
)
4851 compose_wrap_all_full(compose
, FALSE
);
4854 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4856 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4857 GtkTextBuffer
*buffer
;
4859 gboolean modified
= TRUE
;
4861 buffer
= gtk_text_view_get_buffer(text
);
4863 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4865 undo_wrapping(compose
->undostruct
, TRUE
);
4867 while (!gtk_text_iter_is_end(&iter
) && modified
)
4868 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4870 undo_wrapping(compose
->undostruct
, FALSE
);
4874 static void compose_set_title(Compose
*compose
)
4880 edited
= compose
->modified
? _(" [Edited]") : "";
4882 subject
= gtk_editable_get_chars(
4883 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4885 #ifndef GENERIC_UMPC
4886 if (subject
&& strlen(subject
))
4887 str
= g_strdup_printf(_("%s - Compose message%s"),
4890 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4892 str
= g_strdup(_("Compose message"));
4895 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4901 * compose_current_mail_account:
4903 * Find a current mail account (the currently selected account, or the
4904 * default account, if a news account is currently selected). If a
4905 * mail account cannot be found, display an error message.
4907 * Return value: Mail account, or NULL if not found.
4909 static PrefsAccount
*
4910 compose_current_mail_account(void)
4914 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4917 ac
= account_get_default();
4918 if (!ac
|| ac
->protocol
== A_NNTP
) {
4919 alertpanel_error(_("Account for sending mail is not specified.\n"
4920 "Please select a mail account before sending."));
4927 #define QUOTE_IF_REQUIRED(out, str) \
4929 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4933 len = strlen(str) + 3; \
4934 if ((__tmp = alloca(len)) == NULL) { \
4935 g_warning("can't allocate memory"); \
4936 g_string_free(header, TRUE); \
4939 g_snprintf(__tmp, len, "\"%s\"", str); \
4944 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4945 g_warning("can't allocate memory"); \
4946 g_string_free(header, TRUE); \
4949 strcpy(__tmp, str); \
4955 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4957 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4961 len = strlen(str) + 3; \
4962 if ((__tmp = alloca(len)) == NULL) { \
4963 g_warning("can't allocate memory"); \
4966 g_snprintf(__tmp, len, "\"%s\"", str); \
4971 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4972 g_warning("can't allocate memory"); \
4975 strcpy(__tmp, str); \
4981 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4984 gchar
*from
= NULL
, *header
= NULL
;
4985 ComposeHeaderEntry
*header_entry
;
4988 cm_return_if_fail(account
!= NULL
);
4990 compose
->account
= account
;
4991 if (account
->name
&& *account
->name
) {
4993 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4994 qbuf
= escape_internal_quotes(buf
, '"');
4995 from
= g_strdup_printf("%s <%s>",
4996 qbuf
, account
->address
);
4999 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
5001 from
= g_strdup_printf("<%s>",
5003 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
5008 compose_set_title(compose
);
5010 compose_activate_privacy_system(compose
, account
, FALSE
);
5012 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
5013 compose
->mode
!= COMPOSE_REDIRECT
)
5014 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
5016 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
5017 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
5018 compose
->mode
!= COMPOSE_REDIRECT
)
5019 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
5021 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
5023 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
5024 undo_block(compose
->undostruct
);
5025 compose_insert_sig(compose
, TRUE
);
5026 undo_unblock(compose
->undostruct
);
5029 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5030 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5031 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5032 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5034 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5035 if (account
->protocol
== A_NNTP
) {
5036 if (!strcmp(header
, _("To:")))
5037 combobox_select_by_text(
5038 GTK_COMBO_BOX(header_entry
->combo
),
5041 if (!strcmp(header
, _("Newsgroups:")))
5042 combobox_select_by_text(
5043 GTK_COMBO_BOX(header_entry
->combo
),
5051 /* use account's dict info if set */
5052 if (compose
->gtkaspell
) {
5053 if (account
->enable_default_dictionary
)
5054 gtkaspell_change_dict(compose
->gtkaspell
,
5055 account
->default_dictionary
, FALSE
);
5056 if (account
->enable_default_alt_dictionary
)
5057 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5058 account
->default_alt_dictionary
);
5059 if (account
->enable_default_dictionary
5060 || account
->enable_default_alt_dictionary
)
5061 compose_spell_menu_changed(compose
);
5066 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5067 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5068 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5069 gboolean recipient_found
= FALSE
;
5073 /* free to and newsgroup list */
5074 slist_free_strings_full(compose
->to_list
);
5075 compose
->to_list
= NULL
;
5077 slist_free_strings_full(compose
->newsgroup_list
);
5078 compose
->newsgroup_list
= NULL
;
5080 /* search header entries for to and newsgroup entries */
5081 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5084 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5085 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5088 if (entry
[0] != '\0') {
5089 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5090 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5091 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5092 recipient_found
= TRUE
;
5095 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5096 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5097 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5098 recipient_found
= TRUE
;
5105 return recipient_found
;
5108 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5110 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5111 gboolean found_other
= FALSE
;
5113 /* search header entries for to and newsgroup entries */
5114 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5117 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5118 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5121 if (strcmp(entry
, compose
->account
->auto_cc
)
5122 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5133 if (compose
->batch
) {
5134 gtk_widget_show_all(compose
->window
);
5136 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5137 prefs_common_translated_header_name("Cc"));
5138 aval
= alertpanel(_("Send"),
5140 NULL
, _("_Cancel"), NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
);
5142 if (aval
!= G_ALERTALTERNATE
)
5146 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5147 gboolean found_other
= FALSE
;
5149 /* search header entries for to and newsgroup entries */
5150 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5153 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5154 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5157 if (strcmp(entry
, compose
->account
->auto_bcc
)
5158 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5170 if (compose
->batch
) {
5171 gtk_widget_show_all(compose
->window
);
5173 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5174 prefs_common_translated_header_name("Bcc"));
5175 aval
= alertpanel(_("Send"),
5177 NULL
, _("_Cancel"), NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
);
5179 if (aval
!= G_ALERTALTERNATE
)
5186 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5190 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5191 if (compose
->batch
) {
5192 gtk_widget_show_all(compose
->window
);
5194 alertpanel_error(_("Recipient is not specified."));
5198 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5202 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5203 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5204 if (*str
== '\0' && check_everything
== TRUE
&&
5205 compose
->mode
!= COMPOSE_REDIRECT
) {
5209 message
= g_strdup_printf(_("Subject is empty. %s"),
5210 compose
->sending
?_("Send it anyway?"):
5211 _("Queue it anyway?"));
5213 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5214 NULL
, _("_Cancel"), NULL
, compose
->sending
?_("_Send"):_("_Queue"),
5215 NULL
, NULL
, ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5217 if (aval
& G_ALERTDISABLE
) {
5218 aval
&= ~G_ALERTDISABLE
;
5219 prefs_common
.warn_empty_subj
= FALSE
;
5221 if (aval
!= G_ALERTALTERNATE
)
5226 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5227 && check_everything
== TRUE
) {
5231 /* count To and Cc recipients */
5232 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5236 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5237 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5240 if ((entry
[0] != '\0') &&
5241 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5242 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5248 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5252 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5253 compose
->sending
?_("Send it anyway?"):
5254 _("Queue it anyway?"));
5256 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5257 NULL
, _("_Cancel"), NULL
, compose
->sending
?_("_Send"):_("_Queue"),
5258 NULL
, NULL
, ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5260 if (aval
& G_ALERTDISABLE
) {
5261 aval
&= ~G_ALERTDISABLE
;
5262 prefs_common
.warn_sending_many_recipients_num
= 0;
5264 if (aval
!= G_ALERTALTERNATE
)
5269 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5275 static void _display_queue_error(ComposeQueueResult val
)
5278 case COMPOSE_QUEUE_SUCCESS
:
5280 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5281 alertpanel_error(_("Could not queue message."));
5283 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5284 alertpanel_error(_("Could not queue message:\n\n%s."),
5287 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5288 alertpanel_error(_("Could not queue message for sending:\n\n"
5289 "Signature failed: %s"),
5290 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5292 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5293 alertpanel_error(_("Could not queue message for sending:\n\n"
5294 "Encryption failed: %s"),
5295 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5297 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5298 alertpanel_error(_("Could not queue message for sending:\n\n"
5299 "Charset conversion failed."));
5301 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5302 alertpanel_error(_("Could not queue message for sending:\n\n"
5303 "Couldn't get recipient encryption key."));
5305 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5306 debug_print("signing cancelled\n");
5309 /* unhandled error */
5310 debug_print("oops, unhandled compose_queue() return value %d\n",
5316 gint
compose_send(Compose
*compose
)
5319 FolderItem
*folder
= NULL
;
5320 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5321 gchar
*msgpath
= NULL
;
5322 gboolean discard_window
= FALSE
;
5323 gchar
*errstr
= NULL
;
5324 gchar
*tmsgid
= NULL
;
5325 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5326 gboolean queued_removed
= FALSE
;
5328 if (prefs_common
.send_dialog_invisible
5329 || compose
->batch
== TRUE
)
5330 discard_window
= TRUE
;
5332 compose_allow_user_actions (compose
, FALSE
);
5333 compose
->sending
= TRUE
;
5335 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5336 if (compose
->batch
) {
5337 gtk_widget_show_all(compose
->window
);
5343 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5345 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5346 if (compose
->batch
) {
5347 gtk_widget_show_all(compose
->window
);
5350 _display_queue_error(val
);
5355 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5356 if (discard_window
) {
5357 compose
->sending
= FALSE
;
5358 compose_close(compose
);
5359 /* No more compose access in the normal codepath
5360 * after this point! */
5365 alertpanel_error(_("The message was queued but could not be "
5366 "sent.\nUse \"Send queued messages\" from "
5367 "the main window to retry."));
5368 if (!discard_window
) {
5375 if (msgpath
== NULL
) {
5376 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5377 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5380 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5381 claws_unlink(msgpath
);
5384 if (!discard_window
) {
5386 if (!queued_removed
)
5387 folder_item_remove_msg(folder
, msgnum
);
5388 folder_item_scan(folder
);
5390 /* make sure we delete that */
5391 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5393 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5394 folder_item_remove_msg(folder
, tmp
->msgnum
);
5395 procmsg_msginfo_free(&tmp
);
5402 if (!queued_removed
)
5403 folder_item_remove_msg(folder
, msgnum
);
5404 folder_item_scan(folder
);
5406 /* make sure we delete that */
5407 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5409 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5410 folder_item_remove_msg(folder
, tmp
->msgnum
);
5411 procmsg_msginfo_free(&tmp
);
5414 if (!discard_window
) {
5415 compose
->sending
= FALSE
;
5416 compose_allow_user_actions (compose
, TRUE
);
5417 compose_close(compose
);
5421 alertpanel_error_log(_("%s\nYou can try to \"Send\" again "
5422 "or queue the message with \"Send later\""), errstr
);
5425 alertpanel_error_log(_("The message was queued but could not be "
5426 "sent.\nUse \"Send queued messages\" from "
5427 "the main window to retry."));
5429 if (!discard_window
) {
5438 toolbar_main_set_sensitive(mainwin
);
5439 main_window_set_menu_sensitive(mainwin
);
5445 compose_allow_user_actions (compose
, TRUE
);
5446 compose
->sending
= FALSE
;
5447 compose
->modified
= TRUE
;
5448 toolbar_main_set_sensitive(mainwin
);
5449 main_window_set_menu_sensitive(mainwin
);
5454 static gboolean
compose_use_attach(Compose
*compose
)
5456 GtkTreeModel
*model
= gtk_tree_view_get_model
5457 (GTK_TREE_VIEW(compose
->attach_clist
));
5458 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5461 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5464 gchar buf
[BUFFSIZE
];
5466 gboolean first_to_address
;
5467 gboolean first_cc_address
;
5469 ComposeHeaderEntry
*headerentry
;
5470 const gchar
*headerentryname
;
5471 const gchar
*cc_hdr
;
5472 const gchar
*to_hdr
;
5473 gboolean err
= FALSE
;
5475 debug_print("Writing redirect header\n");
5477 cc_hdr
= prefs_common_translated_header_name("Cc:");
5478 to_hdr
= prefs_common_translated_header_name("To:");
5480 first_to_address
= TRUE
;
5481 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5482 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5483 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5485 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5486 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5487 Xstrdup_a(str
, entstr
, return -1);
5489 if (str
[0] != '\0') {
5490 compose_convert_header
5491 (compose
, buf
, sizeof(buf
), str
,
5492 strlen("Resent-To") + 2, TRUE
);
5494 if (first_to_address
) {
5495 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5496 first_to_address
= FALSE
;
5498 err
|= (fprintf(fp
, ",") < 0);
5500 err
|= (fprintf(fp
, "%s", buf
) < 0);
5504 if (!first_to_address
) {
5505 err
|= (fprintf(fp
, "\n") < 0);
5508 first_cc_address
= TRUE
;
5509 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5510 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5511 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5513 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5514 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5515 Xstrdup_a(str
, strg
, return -1);
5517 if (str
[0] != '\0') {
5518 compose_convert_header
5519 (compose
, buf
, sizeof(buf
), str
,
5520 strlen("Resent-Cc") + 2, TRUE
);
5522 if (first_cc_address
) {
5523 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5524 first_cc_address
= FALSE
;
5526 err
|= (fprintf(fp
, ",") < 0);
5528 err
|= (fprintf(fp
, "%s", buf
) < 0);
5532 if (!first_cc_address
) {
5533 err
|= (fprintf(fp
, "\n") < 0);
5536 return (err
? -1:0);
5539 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5541 gchar date
[RFC822_DATE_BUFFSIZE
];
5542 gchar buf
[BUFFSIZE
];
5544 const gchar
*entstr
;
5545 /* struct utsname utsbuf; */
5546 gboolean err
= FALSE
;
5548 cm_return_val_if_fail(fp
!= NULL
, -1);
5549 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5550 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5553 if (prefs_common
.hide_timezone
)
5554 get_rfc822_date_hide_tz(date
, sizeof(date
));
5556 get_rfc822_date(date
, sizeof(date
));
5557 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5560 if (compose
->account
->name
&& *compose
->account
->name
) {
5561 compose_convert_header
5562 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5563 strlen("From: "), TRUE
);
5564 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5565 buf
, compose
->account
->address
) < 0);
5567 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5570 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5571 if (*entstr
!= '\0') {
5572 Xstrdup_a(str
, entstr
, return -1);
5575 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5576 strlen("Subject: "), FALSE
);
5577 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5581 /* Resent-Message-ID */
5582 if (compose
->account
->gen_msgid
) {
5583 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5584 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5586 g_free(compose
->msgid
);
5587 compose
->msgid
= addr
;
5589 compose
->msgid
= NULL
;
5592 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5595 /* separator between header and body */
5596 err
|= (claws_fputs("\n", fp
) == EOF
);
5598 return (err
? -1:0);
5601 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5606 gchar rewrite_buf
[BUFFSIZE
];
5608 gboolean skip
= FALSE
;
5609 gboolean err
= FALSE
;
5610 gchar
*not_included
[]={
5611 "Return-Path:", "Delivered-To:", "Received:",
5612 "Subject:", "X-UIDL:", "AF:",
5613 "NF:", "PS:", "SRH:",
5614 "SFN:", "DSR:", "MID:",
5615 "CFG:", "PT:", "S:",
5616 "RQ:", "SSV:", "NSV:",
5617 "SSH:", "R:", "MAID:",
5618 "NAID:", "RMID:", "FMID:",
5619 "SCF:", "RRCPT:", "NG:",
5620 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5621 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5622 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5623 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5624 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5629 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5630 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5634 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5636 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5637 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5638 strlen(not_included
[i
])) == 0) {
5648 if (claws_fputs(buf
, fdest
) == -1) {
5654 if (!prefs_common
.redirect_keep_from
) {
5655 if (g_ascii_strncasecmp(buf
, "From:",
5656 strlen("From:")) == 0) {
5657 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5658 if (compose
->account
->name
5659 && *compose
->account
->name
) {
5660 gchar buffer
[BUFFSIZE
];
5662 compose_convert_header
5663 (compose
, buffer
, sizeof(buffer
),
5664 compose
->account
->name
,
5667 err
|= (fprintf(fdest
, "%s <%s>",
5669 compose
->account
->address
) < 0);
5671 err
|= (fprintf(fdest
, "%s",
5672 compose
->account
->address
) < 0);
5673 err
|= (claws_fputs(")", fdest
) == EOF
);
5679 if (claws_fputs("\n", fdest
) == -1)
5686 if (compose_redirect_write_headers(compose
, fdest
))
5689 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5690 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5704 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5706 GtkTextBuffer
*buffer
;
5707 GtkTextIter start
, end
, tmp
;
5708 gchar
*chars
, *tmp_enc_file
= NULL
, *content
;
5710 const gchar
*out_codeset
;
5711 EncodingType encoding
= ENC_UNKNOWN
;
5712 MimeInfo
*mimemsg
, *mimetext
;
5714 const gchar
*src_codeset
= CS_INTERNAL
;
5715 gchar
*from_addr
= NULL
;
5716 gchar
*from_name
= NULL
;
5719 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5720 attach_parts
= TRUE
;
5722 /* We're sending the message, generate a Message-ID
5724 if (compose
->msgid
== NULL
&&
5725 compose
->account
->gen_msgid
) {
5726 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5730 /* create message MimeInfo */
5731 mimemsg
= procmime_mimeinfo_new();
5732 mimemsg
->type
= MIMETYPE_MESSAGE
;
5733 mimemsg
->subtype
= g_strdup("rfc822");
5734 mimemsg
->content
= MIMECONTENT_MEM
;
5735 mimemsg
->tmp
= TRUE
; /* must free content later */
5736 mimemsg
->data
.mem
= compose_get_header(compose
);
5738 /* Create text part MimeInfo */
5739 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5740 gtk_text_buffer_get_end_iter(buffer
, &end
);
5743 /* We make sure that there is a newline at the end. */
5744 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5745 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5746 if (*chars
!= '\n') {
5747 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5752 /* get all composed text */
5753 gtk_text_buffer_get_start_iter(buffer
, &start
);
5754 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5756 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5758 if (!out_codeset
&& is_ascii_str(chars
)) {
5759 out_codeset
= CS_US_ASCII
;
5760 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5761 is_ascii_str(chars
)) {
5762 out_codeset
= CS_US_ASCII
;
5763 encoding
= ENC_7BIT
;
5767 gchar
*test_conv_global_out
= NULL
;
5768 gchar
*test_conv_reply
= NULL
;
5770 /* automatic mode. be automatic. */
5771 codeconv_set_strict(TRUE
);
5773 out_codeset
= conv_get_outgoing_charset_str();
5775 debug_print("trying to convert to %s\n", out_codeset
);
5776 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5779 if (!test_conv_global_out
&& compose
->orig_charset
5780 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5781 out_codeset
= compose
->orig_charset
;
5782 debug_print("failure; trying to convert to %s\n", out_codeset
);
5783 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5786 if (!test_conv_global_out
&& !test_conv_reply
) {
5788 out_codeset
= CS_INTERNAL
;
5789 debug_print("failure; finally using %s\n", out_codeset
);
5791 g_free(test_conv_global_out
);
5792 g_free(test_conv_reply
);
5793 codeconv_set_strict(FALSE
);
5796 if (encoding
== ENC_UNKNOWN
) {
5797 if (prefs_common
.encoding_method
== CTE_BASE64
)
5798 encoding
= ENC_BASE64
;
5799 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5800 encoding
= ENC_QUOTED_PRINTABLE
;
5801 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5802 encoding
= ENC_8BIT
;
5804 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5807 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5808 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5810 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5811 codeconv_set_strict(TRUE
);
5812 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5813 codeconv_set_strict(FALSE
);
5818 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5819 "to the specified %s charset.\n"
5820 "Send it as %s?"), out_codeset
, src_codeset
);
5821 aval
= alertpanel_full(_("Error"), msg
, NULL
, _("_Cancel"),
5822 NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
, FALSE
,
5826 if (aval
!= G_ALERTALTERNATE
) {
5828 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5831 out_codeset
= src_codeset
;
5837 out_codeset
= src_codeset
;
5842 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5843 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5844 strstr(buf
, "\nFrom ") != NULL
) {
5845 encoding
= ENC_QUOTED_PRINTABLE
;
5849 mimetext
= procmime_mimeinfo_new();
5850 mimetext
->content
= MIMECONTENT_MEM
;
5851 mimetext
->tmp
= TRUE
; /* must free content later */
5852 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5853 * and free the data, which we need later. */
5854 mimetext
->data
.mem
= g_strdup(buf
);
5855 mimetext
->type
= MIMETYPE_TEXT
;
5856 mimetext
->subtype
= g_strdup("plain");
5857 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5858 g_strdup(out_codeset
));
5860 /* protect trailing spaces when signing message */
5861 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5862 privacy_system_can_sign(compose
->privacy_system
)) {
5863 encoding
= ENC_QUOTED_PRINTABLE
;
5866 debug_print("main text: %" G_GSIZE_FORMAT
" bytes encoded as %s in %d\n",
5867 strlen(buf
), out_codeset
, encoding
);
5869 /* check for line length limit */
5870 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5871 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5872 check_line_length(buf
, 1000, &line
) < 0) {
5875 msg
= g_strdup_printf
5876 (_("Line %d exceeds the line length limit (998 bytes).\n"
5877 "The contents of the message might be broken on the way to the delivery.\n"
5879 "Send it anyway?"), line
+ 1);
5880 aval
= alertpanel(_("Warning"), msg
, NULL
, _("_Cancel"), NULL
, _("_OK"),
5881 NULL
, NULL
, ALERTFOCUS_FIRST
);
5883 if (aval
!= G_ALERTALTERNATE
) {
5885 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5889 if (encoding
!= ENC_UNKNOWN
)
5890 procmime_encode_content(mimetext
, encoding
);
5892 /* append attachment parts */
5893 if (compose_use_attach(compose
) && attach_parts
) {
5894 MimeInfo
*mimempart
;
5895 gchar
*boundary
= NULL
;
5896 mimempart
= procmime_mimeinfo_new();
5897 mimempart
->content
= MIMECONTENT_EMPTY
;
5898 mimempart
->type
= MIMETYPE_MULTIPART
;
5899 mimempart
->subtype
= g_strdup("mixed");
5903 boundary
= generate_mime_boundary(NULL
);
5904 } while (strstr(buf
, boundary
) != NULL
);
5906 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5909 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5911 g_node_append(mimempart
->node
, mimetext
->node
);
5912 g_node_append(mimemsg
->node
, mimempart
->node
);
5914 if (compose_add_attachments(compose
, mimempart
) < 0)
5915 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5917 g_node_append(mimemsg
->node
, mimetext
->node
);
5921 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5922 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5923 /* extract name and address */
5924 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5925 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5926 *(strrchr(from_addr
, '>')) = '\0';
5927 from_name
= g_strdup(spec
);
5928 *(strrchr(from_name
, '<')) = '\0';
5935 /* sign message if sending */
5936 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5937 privacy_system_can_sign(compose
->privacy_system
))
5938 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5939 compose
->account
, from_addr
)) {
5942 if (!privacy_peek_error())
5943 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5945 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5950 if (compose
->use_encryption
) {
5951 if (compose
->encdata
!= NULL
&&
5952 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5954 /* First, write an unencrypted copy and save it to outbox, if
5955 * user wants that. */
5956 if (compose
->account
->save_encrypted_as_clear_text
) {
5957 debug_print("saving sent message unencrypted...\n");
5958 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5960 claws_fclose(tmpfp
);
5962 /* fp now points to a file with headers written,
5963 * let's make a copy. */
5965 content
= file_read_stream_to_str(fp
);
5967 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5970 /* Now write the unencrypted body. */
5971 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5972 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5973 claws_fclose(tmpfp
);
5975 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5977 outbox
= folder_get_default_outbox();
5979 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5980 claws_unlink(tmp_enc_file
);
5982 g_warning("can't open file '%s'", tmp_enc_file
);
5985 g_warning("couldn't get tempfile");
5988 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5989 debug_print("Couldn't encrypt mime structure: %s.\n",
5990 privacy_get_error());
5992 g_free(tmp_enc_file
);
5993 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5998 g_free(tmp_enc_file
);
6000 procmime_write_mimeinfo(mimemsg
, fp
);
6002 procmime_mimeinfo_free_all(&mimemsg
);
6007 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
6009 GtkTextBuffer
*buffer
;
6010 GtkTextIter start
, end
;
6015 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
6016 FILE_OP_ERROR(file
, "claws_fopen");
6020 /* chmod for security */
6021 if (change_file_mode_rw(fp
, file
) < 0) {
6022 FILE_OP_ERROR(file
, "chmod");
6023 g_warning("can't change file mode");
6026 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
6027 gtk_text_buffer_get_start_iter(buffer
, &start
);
6028 gtk_text_buffer_get_end_iter(buffer
, &end
);
6029 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6031 chars
= conv_codeset_strdup
6032 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6041 len
= strlen(chars
);
6042 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6043 FILE_OP_ERROR(file
, "claws_fwrite");
6052 if (claws_safe_fclose(fp
) == EOF
) {
6053 FILE_OP_ERROR(file
, "claws_fclose");
6060 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6063 MsgInfo
*msginfo
= compose
->targetinfo
;
6065 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6066 if (!msginfo
) return -1;
6068 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6071 item
= msginfo
->folder
;
6072 cm_return_val_if_fail(item
!= NULL
, -1);
6074 if (procmsg_msg_exist(msginfo
) &&
6075 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6076 folder_has_parent_of_type(item
, F_DRAFT
)
6077 || msginfo
== compose
->autosaved_draft
)) {
6078 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6079 g_warning("can't remove the old message");
6082 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6089 static void compose_remove_draft(Compose
*compose
)
6092 MsgInfo
*msginfo
= compose
->targetinfo
;
6093 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6095 if (procmsg_msg_exist(msginfo
)) {
6096 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6101 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6102 gboolean remove_reedit_target
)
6104 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6107 static gboolean
compose_warn_encryption(Compose
*compose
)
6109 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6110 AlertValue val
= G_ALERTALTERNATE
;
6112 if (warning
== NULL
)
6115 val
= alertpanel_full(_("Encryption warning"), warning
,
6116 NULL
, _("_Cancel"), NULL
, _("C_ontinue"), NULL
, NULL
,
6117 ALERTFOCUS_SECOND
, TRUE
, NULL
, ALERT_WARNING
);
6118 if (val
& G_ALERTDISABLE
) {
6119 val
&= ~G_ALERTDISABLE
;
6120 if (val
== G_ALERTALTERNATE
)
6121 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6125 if (val
== G_ALERTALTERNATE
) {
6132 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6133 gchar
**msgpath
, gboolean perform_checks
,
6134 gboolean remove_reedit_target
)
6141 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6142 gboolean err
= FALSE
;
6144 debug_print("queueing message...\n");
6145 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6147 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6148 if (compose
->batch
) {
6149 gtk_widget_show_all(compose
->window
);
6151 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6154 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6155 g_warning("can't get recipient list");
6156 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6159 if (compose
->to_list
) {
6160 mailac
= compose
->account
;
6161 if (!mailac
&& cur_account
&& cur_account
->protocol
!= A_NNTP
)
6162 mailac
= cur_account
;
6163 else if (!mailac
&& !(mailac
= compose_current_mail_account())) {
6164 alertpanel_error(_("No account for sending mails available!"));
6165 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6169 if (compose
->newsgroup_list
) {
6170 if (compose
->account
->protocol
== A_NNTP
)
6171 newsac
= compose
->account
;
6173 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6174 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6178 /* write queue header */
6179 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6180 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6181 debug_print("queuing to %s\n", tmp
);
6182 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6183 FILE_OP_ERROR(tmp
, "claws_fopen");
6185 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6188 if (change_file_mode_rw(fp
, tmp
) < 0) {
6189 FILE_OP_ERROR(tmp
, "chmod");
6190 g_warning("can't change file mode");
6193 /* queueing variables */
6194 err
|= (fprintf(fp
, "AF:\n") < 0);
6195 err
|= (fprintf(fp
, "NF:0\n") < 0);
6196 err
|= (fprintf(fp
, "PS:10\n") < 0);
6197 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6198 err
|= (fprintf(fp
, "SFN:\n") < 0);
6199 err
|= (fprintf(fp
, "DSR:\n") < 0);
6201 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6203 err
|= (fprintf(fp
, "MID:\n") < 0);
6204 err
|= (fprintf(fp
, "CFG:\n") < 0);
6205 err
|= (fprintf(fp
, "PT:0\n") < 0);
6206 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6207 err
|= (fprintf(fp
, "RQ:\n") < 0);
6209 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6211 err
|= (fprintf(fp
, "SSV:\n") < 0);
6213 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6215 err
|= (fprintf(fp
, "NSV:\n") < 0);
6216 err
|= (fprintf(fp
, "SSH:\n") < 0);
6217 /* write recipient list */
6218 if (compose
->to_list
) {
6219 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6220 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6222 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6223 err
|= (fprintf(fp
, "\n") < 0);
6225 /* write newsgroup list */
6226 if (compose
->newsgroup_list
) {
6227 err
|= (fprintf(fp
, "NG:") < 0);
6228 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6229 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6230 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6231 err
|= (fprintf(fp
, "\n") < 0);
6235 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6237 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6240 if (compose
->privacy_system
!= NULL
) {
6241 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6242 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6243 if (compose
->use_encryption
) {
6244 if (!compose_warn_encryption(compose
)) {
6248 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6250 if (mailac
&& mailac
->encrypt_to_self
) {
6251 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6252 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6253 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6254 g_slist_free(tmp_list
);
6256 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6258 if (compose
->encdata
!= NULL
) {
6259 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6260 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6261 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6262 compose
->encdata
) < 0);
6263 } /* else we finally dont want to encrypt */
6265 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6266 /* and if encdata was null, it means there's been a problem in
6269 g_warning("failed to write queue message");
6273 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6278 /* Save copy folder */
6279 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6280 gchar
*savefolderid
;
6282 savefolderid
= compose_get_save_to(compose
);
6283 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6284 g_free(savefolderid
);
6286 /* Save copy folder */
6287 if (compose
->return_receipt
) {
6288 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6290 /* Message-ID of message replying to */
6291 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6292 gchar
*folderid
= NULL
;
6294 if (compose
->replyinfo
->folder
)
6295 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6296 if (folderid
== NULL
)
6297 folderid
= g_strdup("NULL");
6299 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6302 /* Message-ID of message forwarding to */
6303 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6304 gchar
*folderid
= NULL
;
6306 if (compose
->fwdinfo
->folder
)
6307 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6308 if (folderid
== NULL
)
6309 folderid
= g_strdup("NULL");
6311 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6315 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6316 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6318 /* end of headers */
6319 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6321 if (compose
->redirect_filename
!= NULL
) {
6322 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6326 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6330 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6338 g_warning("failed to write queue message");
6342 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6344 if (claws_safe_fclose(fp
) == EOF
) {
6345 FILE_OP_ERROR(tmp
, "claws_fclose");
6348 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6351 if (item
&& *item
) {
6354 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6357 g_warning("can't find queue folder");
6360 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6362 folder_item_scan(queue
);
6363 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6364 g_warning("can't queue the message");
6367 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6370 if (msgpath
== NULL
) {
6376 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6377 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6379 procmsg_msginfo_change_flags(mi
,
6380 compose
->targetinfo
->flags
.perm_flags
,
6381 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6384 g_slist_free(mi
->tags
);
6385 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6386 procmsg_msginfo_free(&mi
);
6390 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6391 compose_remove_reedit_target(compose
, FALSE
);
6394 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6399 return COMPOSE_QUEUE_SUCCESS
;
6402 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6405 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6410 GError
*error
= NULL
;
6415 gchar
*type
, *subtype
;
6416 GtkTreeModel
*model
;
6419 model
= gtk_tree_view_get_model(tree_view
);
6421 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6424 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6426 if (!is_file_exist(ainfo
->file
)) {
6427 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6428 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6429 NULL
, _("Cancel sending"),
6430 NULL
, _("Ignore attachment"), NULL
, NULL
,
6431 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6433 if (val
== G_ALERTDEFAULT
) {
6439 f
= g_file_new_for_path(ainfo
->file
);
6440 fi
= g_file_query_info(f
, "standard::size",
6441 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6442 if (error
!= NULL
) {
6443 g_warning(error
->message
);
6444 g_error_free(error
);
6448 size
= g_file_info_get_size(fi
);
6452 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6454 size
= statbuf
.st_size
;
6457 mimepart
= procmime_mimeinfo_new();
6458 mimepart
->content
= MIMECONTENT_FILE
;
6459 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6460 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6461 mimepart
->offset
= 0;
6462 mimepart
->length
= size
;
6464 type
= g_strdup(ainfo
->content_type
);
6466 if (!strchr(type
, '/')) {
6468 type
= g_strdup("application/octet-stream");
6471 subtype
= strchr(type
, '/') + 1;
6472 *(subtype
- 1) = '\0';
6473 mimepart
->type
= procmime_get_media_type(type
);
6474 mimepart
->subtype
= g_strdup(subtype
);
6477 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6478 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6479 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6480 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6481 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6482 /* Text parts with no name come from multipart/alternative
6483 * forwards. Make sure the recipient won't look at the
6484 * original HTML part by mistake. */
6485 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6486 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6490 g_hash_table_insert(mimepart
->typeparameters
,
6491 g_strdup("charset"), g_strdup(ainfo
->charset
));
6493 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6494 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6495 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6496 g_hash_table_insert(mimepart
->typeparameters
,
6497 g_strdup("name"), g_strdup(ainfo
->name
));
6498 g_hash_table_insert(mimepart
->dispositionparameters
,
6499 g_strdup("filename"), g_strdup(ainfo
->name
));
6500 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6503 if (mimepart
->type
== MIMETYPE_MESSAGE
6504 || mimepart
->type
== MIMETYPE_MULTIPART
)
6505 ainfo
->encoding
= ENC_BINARY
;
6506 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6507 if (ainfo
->encoding
== ENC_7BIT
)
6508 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6509 else if (ainfo
->encoding
== ENC_8BIT
)
6510 ainfo
->encoding
= ENC_BASE64
;
6513 procmime_encode_content(mimepart
, ainfo
->encoding
);
6515 g_node_append(parent
->node
, mimepart
->node
);
6516 } while (gtk_tree_model_iter_next(model
, &iter
));
6521 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6523 GSList
*list
= NULL
, *item
= NULL
;
6524 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6526 list
= address_list_append_with_comments(list
, str
);
6527 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6528 gchar
*spec
= item
->data
;
6529 gchar
*endofname
= strstr(spec
, " <");
6530 if (endofname
!= NULL
) {
6533 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6534 qqname
= escape_internal_quotes(qname
, '"');
6536 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6537 gchar
*addr
= g_strdup(endofname
);
6538 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6539 faddr
= g_strconcat(name
, addr
, NULL
);
6542 debug_print("new auto-quoted address: '%s'\n", faddr
);
6546 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6548 gchar
*tmp
= g_strconcat(result
,
6550 (faddr
!= NULL
)? faddr
: spec
,
6555 if (faddr
!= NULL
) {
6560 slist_free_strings_full(list
);
6565 #define IS_IN_CUSTOM_HEADER(header) \
6566 (compose->account->add_customhdr && \
6567 custom_header_find(compose->account->customhdr_list, header) != NULL)
6569 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6571 /* return the untranslated header name, if header_name is a known
6572 header name, in either its translated or untranslated form, with
6573 or without trailing colon. otherwise, returns header_name. */
6574 gchar
*translated_header_name
;
6575 gchar
*translated_header_name_wcolon
;
6576 const gchar
*untranslated_header_name
;
6577 const gchar
*untranslated_header_name_wcolon
;
6580 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6582 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6583 untranslated_header_name
= HEADERS
[i
].header_name
;
6584 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6586 translated_header_name
= gettext(untranslated_header_name
);
6587 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6589 if (!strcmp(header_name
, untranslated_header_name
) ||
6590 !strcmp(header_name
, translated_header_name
)) {
6591 return untranslated_header_name
;
6593 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6594 !strcmp(header_name
, translated_header_name_wcolon
)) {
6595 return untranslated_header_name_wcolon
;
6599 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6603 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6605 const gchar
*fieldname
,
6606 const gchar
*seperator
)
6608 gchar
*str
, *fieldname_w_colon
;
6609 gboolean add_field
= FALSE
;
6611 ComposeHeaderEntry
*headerentry
;
6612 const gchar
*headerentryname
;
6613 const gchar
*trans_fieldname
;
6616 if (IS_IN_CUSTOM_HEADER(fieldname
))
6619 debug_print("Adding %s-fields\n", fieldname
);
6621 fieldstr
= g_string_sized_new(64);
6623 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6624 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6626 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6627 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6628 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6630 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6631 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6633 str
= compose_quote_list_of_addresses(ustr
);
6635 if (str
!= NULL
&& str
[0] != '\0') {
6637 g_string_append(fieldstr
, seperator
);
6638 g_string_append(fieldstr
, str
);
6647 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6648 compose_convert_header
6649 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6650 strlen(fieldname
) + 2, TRUE
);
6651 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6655 g_free(fieldname_w_colon
);
6656 g_string_free(fieldstr
, TRUE
);
6661 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6663 GString
*sh_header
= g_string_new(" ");
6665 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6667 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6668 ComposeHeaderEntry
*headerentry
;
6671 gchar
*headername_wcolon
;
6672 const gchar
*headername_trans
;
6674 gboolean standard_header
= FALSE
;
6676 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6678 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6680 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6685 if (!strstr(tmp
, ":")) {
6686 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6687 headername
= g_strdup(tmp
);
6689 headername_wcolon
= g_strdup(tmp
);
6690 headername
= g_strdup(strtok(tmp
, ":"));
6694 string
= std_headers
;
6695 while (*string
!= NULL
) {
6696 headername_trans
= prefs_common_translated_header_name(*string
);
6697 if (!strcmp(headername_trans
, headername_wcolon
))
6698 standard_header
= TRUE
;
6701 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6702 g_string_append_printf(sh_header
, "%s ", headername
);
6704 g_free(headername_wcolon
);
6706 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6707 return g_string_free(sh_header
, FALSE
);
6710 static gchar
*compose_get_header(Compose
*compose
)
6712 gchar date
[RFC822_DATE_BUFFSIZE
];
6713 gchar buf
[BUFFSIZE
];
6714 const gchar
*entry_str
;
6718 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6720 gchar
*from_name
= NULL
, *from_address
= NULL
;
6723 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6724 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6726 header
= g_string_sized_new(64);
6729 if (prefs_common
.hide_timezone
)
6730 get_rfc822_date_hide_tz(date
, sizeof(date
));
6732 get_rfc822_date(date
, sizeof(date
));
6733 g_string_append_printf(header
, "Date: %s\n", date
);
6737 if (compose
->account
->name
&& *compose
->account
->name
) {
6739 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6740 tmp
= g_strdup_printf("%s <%s>",
6741 buf
, compose
->account
->address
);
6743 tmp
= g_strdup_printf("%s",
6744 compose
->account
->address
);
6746 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6747 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6749 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6750 from_address
= g_strdup(compose
->account
->address
);
6752 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6753 /* extract name and address */
6754 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6755 from_address
= g_strdup(strrchr(spec
, '<')+1);
6756 *(strrchr(from_address
, '>')) = '\0';
6757 from_name
= g_strdup(spec
);
6758 *(strrchr(from_name
, '<')) = '\0';
6761 from_address
= g_strdup(spec
);
6768 if (from_name
&& *from_name
) {
6770 compose_convert_header
6771 (compose
, buf
, sizeof(buf
), from_name
,
6772 strlen("From: "), TRUE
);
6773 QUOTE_IF_REQUIRED(name
, buf
);
6774 qname
= escape_internal_quotes(name
, '"');
6776 g_string_append_printf(header
, "From: %s <%s>\n",
6777 qname
, from_address
);
6778 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6779 compose
->return_receipt
) {
6780 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6781 strlen("Disposition-Notification-To: "),
6783 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6788 g_string_append_printf(header
, "From: %s\n", from_address
);
6789 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6790 compose
->return_receipt
)
6791 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6795 g_free(from_address
);
6798 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6801 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6804 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6808 * If this account is a NNTP account remove Bcc header from
6809 * message body since it otherwise will be publicly shown
6811 if (compose
->account
->protocol
!= A_NNTP
)
6812 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6815 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6817 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6820 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6821 strlen("Subject: "), FALSE
);
6822 g_string_append_printf(header
, "Subject: %s\n", buf
);
6828 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6829 g_string_append_printf(header
, "Message-ID: <%s>\n",
6833 if (compose
->remove_references
== FALSE
) {
6835 if (compose
->inreplyto
&& compose
->to_list
)
6836 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6839 if (compose
->references
)
6840 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6844 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6847 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6850 if (compose
->account
->organization
&&
6851 strlen(compose
->account
->organization
) &&
6852 !IS_IN_CUSTOM_HEADER("Organization")) {
6853 compose_convert_header(compose
, buf
, sizeof(buf
),
6854 compose
->account
->organization
,
6855 strlen("Organization: "), FALSE
);
6856 g_string_append_printf(header
, "Organization: %s\n", buf
);
6859 /* Program version and system info */
6860 if (compose
->account
->gen_xmailer
&&
6861 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6862 !compose
->newsgroup_list
) {
6863 g_string_append_printf(header
, "X-Mailer: %s (GTK %d.%d.%d; %s)\n",
6865 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6868 if (compose
->account
->gen_xmailer
&&
6869 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6870 g_string_append_printf(header
, "X-Newsreader: %s (GTK %d.%d.%d; %s)\n",
6872 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6876 /* custom headers */
6877 if (compose
->account
->add_customhdr
) {
6880 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6882 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6884 if (custom_header_is_allowed(chdr
->name
)
6885 && chdr
->value
!= NULL
6886 && *(chdr
->value
) != '\0') {
6887 compose_convert_header
6888 (compose
, buf
, sizeof(buf
),
6890 strlen(chdr
->name
) + 2, FALSE
);
6891 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6896 /* Automatic Faces and X-Faces */
6897 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6898 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6900 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6901 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6903 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6904 g_string_append_printf(header
, "Face: %s\n", buf
);
6906 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6907 g_string_append_printf(header
, "Face: %s\n", buf
);
6911 switch (compose
->priority
) {
6912 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6913 "X-Priority: 1 (Highest)\n");
6915 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6916 "X-Priority: 2 (High)\n");
6918 case PRIORITY_NORMAL
: break;
6919 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6920 "X-Priority: 4 (Low)\n");
6922 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6923 "X-Priority: 5 (Lowest)\n");
6925 default: debug_print("compose: priority unknown : %d\n",
6929 /* get special headers */
6930 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6931 ComposeHeaderEntry
*headerentry
;
6934 gchar
*headername_wcolon
;
6935 const gchar
*headername_trans
;
6938 gboolean standard_header
= FALSE
;
6940 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6942 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6944 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6949 if (!strstr(tmp
, ":")) {
6950 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6951 headername
= g_strdup(tmp
);
6953 headername_wcolon
= g_strdup(tmp
);
6954 headername
= g_strdup(strtok(tmp
, ":"));
6958 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6959 Xstrdup_a(headervalue
, entry_str
, {
6961 g_free(headername_wcolon
);
6962 g_string_free(header
, TRUE
);
6965 subst_char(headervalue
, '\r', ' ');
6966 subst_char(headervalue
, '\n', ' ');
6967 g_strstrip(headervalue
);
6968 if (*headervalue
!= '\0') {
6969 string
= std_headers
;
6970 while (*string
!= NULL
&& !standard_header
) {
6971 headername_trans
= prefs_common_translated_header_name(*string
);
6972 /* support mixed translated and untranslated headers */
6973 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6974 standard_header
= TRUE
;
6977 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6978 /* store untranslated header name */
6979 g_string_append_printf(header
, "%s %s\n",
6980 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6984 g_free(headername_wcolon
);
6988 g_string_free(header
, FALSE
);
6993 #undef IS_IN_CUSTOM_HEADER
6995 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6996 gint header_len
, gboolean addr_field
)
6998 gchar
*tmpstr
= NULL
;
6999 const gchar
*out_codeset
= NULL
;
7001 cm_return_if_fail(src
!= NULL
);
7002 cm_return_if_fail(dest
!= NULL
);
7004 if (len
< 1) return;
7006 tmpstr
= g_strdup(src
);
7008 subst_char(tmpstr
, '\n', ' ');
7009 subst_char(tmpstr
, '\r', ' ');
7012 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
7013 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
7014 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
7019 codeconv_set_strict(TRUE
);
7020 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7021 conv_get_charset_str(compose
->out_encoding
));
7022 codeconv_set_strict(FALSE
);
7024 if (!dest
|| *dest
== '\0') {
7025 gchar
*test_conv_global_out
= NULL
;
7026 gchar
*test_conv_reply
= NULL
;
7028 /* automatic mode. be automatic. */
7029 codeconv_set_strict(TRUE
);
7031 out_codeset
= conv_get_outgoing_charset_str();
7033 debug_print("trying to convert to %s\n", out_codeset
);
7034 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7037 if (!test_conv_global_out
&& compose
->orig_charset
7038 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7039 out_codeset
= compose
->orig_charset
;
7040 debug_print("failure; trying to convert to %s\n", out_codeset
);
7041 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7044 if (!test_conv_global_out
&& !test_conv_reply
) {
7046 out_codeset
= CS_INTERNAL
;
7047 debug_print("finally using %s\n", out_codeset
);
7049 g_free(test_conv_global_out
);
7050 g_free(test_conv_reply
);
7051 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7053 codeconv_set_strict(FALSE
);
7058 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7062 cm_return_if_fail(user_data
!= NULL
);
7064 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7065 g_strstrip(address
);
7066 if (*address
!= '\0') {
7067 gchar
*name
= procheader_get_fromname(address
);
7068 extract_address(address
);
7069 #ifndef USE_ALT_ADDRBOOK
7070 addressbook_add_contact(name
, address
, NULL
, NULL
);
7072 debug_print("%s: %s\n", name
, address
);
7073 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7074 debug_print( "addressbook_add_contact - added\n" );
7081 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7083 GtkWidget
*menuitem
;
7086 cm_return_if_fail(menu
!= NULL
);
7087 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7089 menuitem
= gtk_separator_menu_item_new();
7090 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7091 gtk_widget_show(menuitem
);
7093 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7094 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7096 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7097 g_strstrip(address
);
7098 if (*address
== '\0') {
7099 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7102 g_signal_connect(G_OBJECT(menuitem
), "activate",
7103 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7104 gtk_widget_show(menuitem
);
7107 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7110 if (strcmp(header
, "")) {
7111 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7115 void compose_add_extra_header_entries(GtkListStore
*model
)
7119 gchar buf
[BUFFSIZE
];
7122 if (extra_headers
== NULL
) {
7123 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7124 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7125 debug_print("extra headers file not found\n");
7126 goto extra_headers_done
;
7128 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7129 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7130 while (lastc
>= 0 && buf
[lastc
] != ':')
7131 buf
[lastc
--] = '\0';
7132 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7133 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7134 if (custom_header_is_allowed(buf
)) {
7136 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7139 g_message("disallowed extra header line: %s\n", buf
);
7143 g_message("invalid extra header line: %s\n", buf
);
7149 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7150 extra_headers
= g_slist_reverse(extra_headers
);
7152 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7156 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7158 LdapServer
*server
= (LdapServer
*)data
;
7159 gboolean
*enable
= (gboolean
*)user_data
;
7161 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7162 server
->searchFlag
= *enable
;
7166 static void compose_create_header_entry(Compose
*compose
)
7168 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7175 const gchar
*header
= NULL
;
7176 ComposeHeaderEntry
*headerentry
;
7177 gboolean standard_header
= FALSE
;
7178 GtkListStore
*model
;
7181 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7183 /* Combo box model */
7184 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7185 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7187 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7189 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7191 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7192 COMPOSE_NEWSGROUPS
);
7193 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7195 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7196 COMPOSE_FOLLOWUPTO
);
7197 compose_add_extra_header_entries(model
);
7200 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7201 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7202 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7203 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7204 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7205 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7206 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7207 G_CALLBACK(compose_grab_focus_cb
), compose
);
7208 gtk_widget_show(combo
);
7210 gtk_grid_attach(GTK_GRID(compose
->header_table
), combo
, 0, compose
->header_nextrow
,
7212 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7213 const gchar
*last_header_entry
= gtk_entry_get_text(
7214 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7216 while (*string
!= NULL
) {
7217 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7218 standard_header
= TRUE
;
7221 if (standard_header
)
7222 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7224 if (!compose
->header_last
|| !standard_header
) {
7225 switch(compose
->account
->protocol
) {
7227 header
= prefs_common_translated_header_name("Newsgroups:");
7230 header
= prefs_common_translated_header_name("To:");
7235 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7237 gtk_editable_set_editable(
7238 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7239 prefs_common
.type_any_header
);
7241 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7242 G_CALLBACK(compose_grab_focus_cb
), compose
);
7244 /* Entry field with cleanup button */
7245 button
= gtk_button_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU
);
7246 gtk_widget_show(button
);
7247 CLAWS_SET_TIP(button
,
7248 _("Delete entry contents"));
7249 entry
= gtk_entry_new();
7250 gtk_widget_show(entry
);
7251 CLAWS_SET_TIP(entry
,
7252 _("Use <tab> to autocomplete from addressbook"));
7253 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
7254 gtk_widget_show(hbox
);
7255 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7256 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7257 gtk_grid_attach(GTK_GRID(compose
->header_table
), hbox
, 1, compose
->header_nextrow
,
7259 gtk_widget_set_hexpand(hbox
, TRUE
);
7260 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
7262 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7263 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7265 g_signal_connect(G_OBJECT(entry
), "changed",
7266 G_CALLBACK(compose_headerentry_changed_cb
),
7268 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7269 G_CALLBACK(compose_grab_focus_cb
), compose
);
7271 g_signal_connect(G_OBJECT(button
), "clicked",
7272 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7276 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7277 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7278 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7279 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7280 G_CALLBACK(compose_header_drag_received_cb
),
7282 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7283 G_CALLBACK(compose_drag_drop
),
7285 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7286 G_CALLBACK(compose_entry_popup_extend
),
7290 #ifndef PASSWORD_CRYPTO_OLD
7291 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7292 if (pwd_servers
!= NULL
&& primary_passphrase() == NULL
) {
7293 gboolean enable
= FALSE
;
7294 debug_print("Primary passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7295 /* Temporarily disable password-protected LDAP servers,
7296 * because user did not provide a primary passphrase.
7297 * We can safely enable searchFlag on all servers in this list
7298 * later, since addrindex_get_password_protected_ldap_servers()
7299 * includes servers which have it enabled initially. */
7300 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7301 compose
->passworded_ldap_servers
= pwd_servers
;
7303 #endif /* PASSWORD_CRYPTO_OLD */
7304 #endif /* USE_LDAP */
7306 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7308 headerentry
->compose
= compose
;
7309 headerentry
->combo
= combo
;
7310 headerentry
->entry
= entry
;
7311 headerentry
->button
= button
;
7312 headerentry
->hbox
= hbox
;
7313 headerentry
->headernum
= compose
->header_nextrow
;
7314 headerentry
->type
= PREF_NONE
;
7316 compose
->header_nextrow
++;
7317 compose
->header_last
= headerentry
;
7318 compose
->header_list
=
7319 g_slist_append(compose
->header_list
,
7323 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7324 gchar
*text
, ComposePrefType pref_type
)
7326 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7327 gchar
*tmp
= g_strdup(text
), *email
;
7328 gboolean replyto_hdr
;
7330 replyto_hdr
= (!strcasecmp(header
,
7331 prefs_common_translated_header_name("Reply-To:")) ||
7333 prefs_common_translated_header_name("Followup-To:")) ||
7335 prefs_common_translated_header_name("In-Reply-To:")));
7337 extract_address(tmp
);
7338 email
= g_utf8_strdown(tmp
, -1);
7340 if (replyto_hdr
== FALSE
&&
7341 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7343 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7344 header
, text
, (gint
) pref_type
);
7350 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7351 gtk_entry_set_text(GTK_ENTRY(
7352 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7354 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7355 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7356 last_header
->type
= pref_type
;
7358 if (replyto_hdr
== FALSE
)
7359 g_hash_table_insert(compose
->email_hashtable
, email
,
7360 GUINT_TO_POINTER(1));
7367 static void compose_destroy_headerentry(Compose
*compose
,
7368 ComposeHeaderEntry
*headerentry
)
7370 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7373 extract_address(text
);
7374 email
= g_utf8_strdown(text
, -1);
7375 g_hash_table_remove(compose
->email_hashtable
, email
);
7379 gtk_widget_destroy(headerentry
->combo
);
7380 gtk_widget_destroy(headerentry
->entry
);
7381 gtk_widget_destroy(headerentry
->button
);
7382 gtk_widget_destroy(headerentry
->hbox
);
7383 g_free(headerentry
);
7386 static void compose_remove_header_entries(Compose
*compose
)
7389 for (list
= compose
->header_list
; list
; list
= list
->next
)
7390 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7392 compose
->header_last
= NULL
;
7393 g_slist_free(compose
->header_list
);
7394 compose
->header_list
= NULL
;
7395 compose
->header_nextrow
= 1;
7396 compose_create_header_entry(compose
);
7399 static GtkWidget
*compose_create_header(Compose
*compose
)
7401 GtkWidget
*from_optmenu_hbox
;
7402 GtkWidget
*header_table_main
;
7403 GtkWidget
*header_scrolledwin
;
7404 GtkWidget
*header_table
;
7406 /* parent with account selection and from header */
7407 header_table_main
= gtk_grid_new();
7408 gtk_widget_show(header_table_main
);
7409 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7411 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7412 gtk_grid_attach(GTK_GRID(header_table_main
),from_optmenu_hbox
, 0, 0, 1, 1);
7413 gtk_widget_set_hexpand(from_optmenu_hbox
, TRUE
);
7414 gtk_widget_set_halign(from_optmenu_hbox
, GTK_ALIGN_FILL
);
7416 /* child with header labels and entries */
7417 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7418 gtk_widget_show(header_scrolledwin
);
7419 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7421 header_table
= gtk_grid_new();
7422 gtk_widget_show(header_table
);
7423 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7424 gtk_container_add(GTK_CONTAINER(header_scrolledwin
), header_table
);
7425 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7426 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7427 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7429 gtk_grid_attach(GTK_GRID(header_table_main
), header_scrolledwin
, 0, 1, 1, 1);
7430 gtk_widget_set_vexpand(header_scrolledwin
, TRUE
);
7431 gtk_widget_set_valign(header_scrolledwin
, GTK_ALIGN_FILL
);
7433 compose
->header_table
= header_table
;
7434 compose
->header_list
= NULL
;
7435 compose
->header_nextrow
= 0;
7437 compose_create_header_entry(compose
);
7439 compose
->table
= NULL
;
7441 return header_table_main
;
7444 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7446 Compose
*compose
= (Compose
*)data
;
7447 GdkEventButton event
;
7450 event
.time
= gtk_get_current_event_time();
7452 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7455 static GtkWidget
*compose_create_attach(Compose
*compose
)
7457 GtkWidget
*attach_scrwin
;
7458 GtkWidget
*attach_clist
;
7460 GtkListStore
*store
;
7461 GtkCellRenderer
*renderer
;
7462 GtkTreeViewColumn
*column
;
7463 GtkTreeSelection
*selection
;
7465 /* attachment list */
7466 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7467 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7468 GTK_POLICY_AUTOMATIC
,
7469 GTK_POLICY_AUTOMATIC
);
7470 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7472 store
= gtk_list_store_new(N_ATTACH_COLS
,
7478 G_TYPE_AUTO_POINTER
,
7480 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7481 (GTK_TREE_MODEL(store
)));
7482 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7483 g_object_unref(store
);
7485 renderer
= gtk_cell_renderer_text_new();
7486 column
= gtk_tree_view_column_new_with_attributes
7487 (_("Mime type"), renderer
, "text",
7488 COL_MIMETYPE
, NULL
);
7489 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7491 renderer
= gtk_cell_renderer_text_new();
7492 column
= gtk_tree_view_column_new_with_attributes
7493 (_("Size"), renderer
, "text",
7495 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7497 renderer
= gtk_cell_renderer_text_new();
7498 column
= gtk_tree_view_column_new_with_attributes
7499 (_("Name"), renderer
, "text",
7501 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7503 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7504 prefs_common
.use_stripes_everywhere
);
7505 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7506 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7508 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7509 G_CALLBACK(attach_selected
), compose
);
7510 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7511 G_CALLBACK(attach_button_pressed
), compose
);
7512 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7513 G_CALLBACK(popup_attach_button_pressed
), compose
);
7514 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7515 G_CALLBACK(attach_key_pressed
), compose
);
7518 gtk_drag_dest_set(attach_clist
,
7519 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7520 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7521 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7522 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7523 G_CALLBACK(compose_attach_drag_received_cb
),
7525 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7526 G_CALLBACK(compose_drag_drop
),
7529 compose
->attach_scrwin
= attach_scrwin
;
7530 compose
->attach_clist
= attach_clist
;
7532 return attach_scrwin
;
7535 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7537 static GtkWidget
*compose_create_others(Compose
*compose
)
7540 GtkWidget
*savemsg_checkbtn
;
7541 GtkWidget
*savemsg_combo
;
7542 GtkWidget
*savemsg_select
;
7545 gchar
*folderidentifier
;
7547 /* Table for settings */
7548 table
= gtk_grid_new();
7549 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7550 gtk_widget_show(table
);
7551 gtk_grid_set_row_spacing(GTK_GRID(table
), VSPACING_NARROW
);
7554 /* Save Message to folder */
7555 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7556 gtk_widget_show(savemsg_checkbtn
);
7557 gtk_grid_attach(GTK_GRID(table
), savemsg_checkbtn
, 0, rowcount
, 1, 1);
7558 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7562 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7563 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7564 compose
->savemsg_combo
= savemsg_combo
;
7565 gtk_widget_show(savemsg_combo
);
7567 if (prefs_common
.compose_save_to_history
)
7568 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7569 prefs_common
.compose_save_to_history
);
7570 gtk_grid_attach(GTK_GRID(table
), savemsg_combo
, 1, rowcount
, 1, 1);
7571 gtk_widget_set_hexpand(savemsg_combo
, TRUE
);
7572 gtk_widget_set_halign(savemsg_combo
, GTK_ALIGN_FILL
);
7573 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7574 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7575 G_CALLBACK(compose_grab_focus_cb
), compose
);
7576 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7577 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7578 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7580 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7581 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7582 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7583 (compose
->account
, F_OUTBOX
));
7584 compose_set_save_to(compose
, folderidentifier
);
7585 g_free(folderidentifier
);
7588 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7589 gtk_widget_show(savemsg_select
);
7590 gtk_grid_attach(GTK_GRID(table
), savemsg_select
, 2, rowcount
, 1, 1);
7591 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7592 G_CALLBACK(compose_savemsg_select_cb
),
7598 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7603 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7604 _("Select folder to save message to"));
7607 path
= folder_item_get_identifier(dest
);
7609 compose_set_save_to(compose
, path
);
7613 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7614 GdkAtom clip
, GtkTextIter
*insert_place
);
7617 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7621 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7623 if (event
->button
== 3) {
7625 GtkTextIter sel_start
, sel_end
;
7626 gboolean stuff_selected
;
7628 /* move the cursor to allow GtkAspell to check the word
7629 * under the mouse */
7630 if (event
->x
&& event
->y
) {
7631 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7632 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7634 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7637 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7638 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7641 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7643 &sel_start
, &sel_end
);
7645 gtk_text_buffer_place_cursor (buffer
, &iter
);
7646 /* reselect stuff */
7648 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7649 gtk_text_buffer_select_range(buffer
,
7650 &sel_start
, &sel_end
);
7652 return FALSE
; /* pass the event so that the right-click goes through */
7655 if (event
->button
== 2) {
7660 /* get the middle-click position to paste at the correct place */
7661 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7662 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7664 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7667 entry_paste_clipboard(compose
, text
,
7668 prefs_common
.linewrap_pastes
,
7669 GDK_SELECTION_PRIMARY
, &iter
);
7677 static void compose_spell_menu_changed(void *data
)
7679 Compose
*compose
= (Compose
*)data
;
7681 GtkWidget
*menuitem
;
7682 GtkWidget
*parent_item
;
7683 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7686 if (compose
->gtkaspell
== NULL
)
7689 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7690 "/Menu/Spelling/Options");
7692 /* setting the submenu removes /Spelling/Options from the factory
7693 * so we need to save it */
7695 if (parent_item
== NULL
) {
7696 parent_item
= compose
->aspell_options_menu
;
7697 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7699 compose
->aspell_options_menu
= parent_item
;
7701 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7703 spell_menu
= g_slist_reverse(spell_menu
);
7704 for (items
= spell_menu
;
7705 items
; items
= items
->next
) {
7706 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7707 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7708 gtk_widget_show(GTK_WIDGET(menuitem
));
7710 g_slist_free(spell_menu
);
7712 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7713 gtk_widget_show(parent_item
);
7716 static void compose_dict_changed(void *data
)
7718 Compose
*compose
= (Compose
*) data
;
7720 if(!compose
->gtkaspell
)
7722 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7725 gtkaspell_highlight_all(compose
->gtkaspell
);
7726 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7730 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7732 Compose
*compose
= (Compose
*)data
;
7733 GdkEventButton event
;
7736 event
.time
= gtk_get_current_event_time();
7740 return text_clicked(compose
->text
, &event
, compose
);
7743 static gboolean compose_force_window_origin
= TRUE
;
7744 static Compose
*compose_create(PrefsAccount
*account
,
7753 GtkWidget
*handlebox
;
7755 GtkWidget
*notebook
;
7757 GtkWidget
*attach_hbox
;
7758 GtkWidget
*attach_lab1
;
7759 GtkWidget
*attach_lab2
;
7764 GtkWidget
*subject_hbox
;
7765 GtkWidget
*subject_frame
;
7766 GtkWidget
*subject_entry
;
7770 GtkWidget
*edit_vbox
;
7771 GtkWidget
*ruler_hbox
;
7773 GtkWidget
*scrolledwin
;
7775 GtkTextBuffer
*buffer
;
7776 GtkClipboard
*clipboard
;
7778 UndoMain
*undostruct
;
7780 GtkWidget
*popupmenu
;
7781 GtkWidget
*tmpl_menu
;
7782 GtkActionGroup
*action_group
= NULL
;
7785 GtkAspell
* gtkaspell
= NULL
;
7788 static GdkGeometry geometry
;
7789 GdkRectangle workarea
= {0};
7791 cm_return_val_if_fail(account
!= NULL
, NULL
);
7793 default_header_bgcolor
= prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7794 default_header_color
= prefs_common
.color
[COL_DEFAULT_HEADER
],
7796 debug_print("Creating compose window...\n");
7797 compose
= g_new0(Compose
, 1);
7799 compose
->batch
= batch
;
7800 compose
->account
= account
;
7801 compose
->folder
= folder
;
7803 g_mutex_init(&compose
->mutex
);
7804 compose
->set_cursor_pos
= -1;
7806 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7808 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7809 gtk_window_set_default_size(GTK_WINDOW(window
), prefs_common
.compose_width
,
7810 prefs_common
.compose_height
);
7812 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
7815 if (!geometry
.max_width
) {
7816 geometry
.max_width
= workarea
.width
;
7817 geometry
.max_height
= workarea
.height
;
7820 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7821 &geometry
, GDK_HINT_MAX_SIZE
);
7822 if (!geometry
.min_width
) {
7823 geometry
.min_width
= 600;
7824 geometry
.min_height
= 440;
7826 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7827 &geometry
, GDK_HINT_MIN_SIZE
);
7829 #ifndef GENERIC_UMPC
7830 if (compose_force_window_origin
)
7831 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7832 prefs_common
.compose_y
);
7834 g_signal_connect(G_OBJECT(window
), "delete_event",
7835 G_CALLBACK(compose_delete_cb
), compose
);
7836 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7837 gtk_widget_realize(window
);
7839 gtkut_widget_set_composer_icon(window
);
7841 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
7842 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7844 compose
->ui_manager
= gtk_ui_manager_new();
7845 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7846 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7847 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7848 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7849 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7850 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7851 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7852 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7853 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7854 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7859 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7863 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7868 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7869 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7870 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7874 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7883 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7885 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7888 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7891 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7896 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7898 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7900 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7906 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7908 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7914 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7918 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7919 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7922 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7923 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7927 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7930 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7936 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7944 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7946 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7949 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7950 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7951 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7952 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7953 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7954 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7955 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7957 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7958 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7959 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7960 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7961 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7963 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7965 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7966 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7967 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7968 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7969 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7971 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7972 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
)
7973 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
)
7974 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7976 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7978 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7979 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
)
7980 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
)
7982 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7984 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7985 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
)
7986 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7988 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7989 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
)
7990 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7992 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7994 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7995 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
)
7996 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
8001 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
8002 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
)
8003 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
)
8004 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
8005 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
8007 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
8008 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
8009 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
8010 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
8011 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
8012 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
8014 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
8015 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
8016 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
)
8018 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
8019 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
8020 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
8024 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
8025 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8026 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8027 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8028 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8029 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8032 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8034 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8035 gtk_widget_show_all(menubar
);
8037 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8038 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8040 handlebox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8041 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8043 gtk_widget_realize(handlebox
);
8044 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8047 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 2);
8048 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8049 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8052 notebook
= gtk_notebook_new();
8053 gtk_widget_show(notebook
);
8055 /* header labels and entries */
8056 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8057 compose_create_header(compose
),
8058 gtk_label_new_with_mnemonic(_("Hea_der")));
8059 /* attachment list */
8060 attach_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8061 gtk_widget_show(attach_hbox
);
8063 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8064 gtk_widget_show(attach_lab1
);
8065 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8067 attach_lab2
= gtk_label_new("");
8068 gtk_widget_show(attach_lab2
);
8069 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8071 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8072 compose_create_attach(compose
),
8075 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8076 compose_create_others(compose
),
8077 gtk_label_new_with_mnemonic(_("Othe_rs")));
8080 subject_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8081 gtk_widget_show(subject_hbox
);
8083 subject_frame
= gtk_frame_new(NULL
);
8084 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8085 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8086 gtk_widget_show(subject_frame
);
8088 subject
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, HSPACING_NARROW
);
8089 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8090 gtk_widget_show(subject
);
8092 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8093 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8094 gtk_widget_show(label
);
8097 subject_entry
= claws_spell_entry_new();
8099 subject_entry
= gtk_entry_new();
8101 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8102 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8103 G_CALLBACK(compose_grab_focus_cb
), compose
);
8104 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8105 gtk_widget_show(subject_entry
);
8106 compose
->subject_entry
= subject_entry
;
8107 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8109 edit_vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
8111 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8114 ruler_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8115 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8117 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8118 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8119 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8123 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8124 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8125 GTK_POLICY_AUTOMATIC
,
8126 GTK_POLICY_AUTOMATIC
);
8127 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8129 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8131 text
= gtk_text_view_new();
8132 if (prefs_common
.show_compose_margin
) {
8133 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8134 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8136 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8137 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8138 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8139 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8140 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8142 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8143 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8144 G_CALLBACK(compose_edit_size_alloc
),
8146 g_signal_connect(G_OBJECT(buffer
), "changed",
8147 G_CALLBACK(compose_changed_cb
), compose
);
8148 g_signal_connect(G_OBJECT(text
), "grab_focus",
8149 G_CALLBACK(compose_grab_focus_cb
), compose
);
8150 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8151 G_CALLBACK(text_inserted
), compose
);
8152 g_signal_connect(G_OBJECT(text
), "button_press_event",
8153 G_CALLBACK(text_clicked
), compose
);
8154 g_signal_connect(G_OBJECT(text
), "popup-menu",
8155 G_CALLBACK(compose_popup_menu
), compose
);
8156 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8157 G_CALLBACK(compose_changed_cb
), compose
);
8158 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8159 G_CALLBACK(compose_subject_entry_activated
), compose
);
8162 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8163 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8164 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8165 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8166 G_CALLBACK(compose_insert_drag_received_cb
),
8168 g_signal_connect(G_OBJECT(text
), "drag-drop",
8169 G_CALLBACK(compose_drag_drop
),
8171 g_signal_connect(G_OBJECT(text
), "key-press-event",
8172 G_CALLBACK(completion_set_focus_to_subject
),
8174 gtk_widget_show_all(vbox
);
8176 /* pane between attach clist and text */
8177 paned
= gtk_paned_new(GTK_ORIENTATION_VERTICAL
);
8178 gtk_box_pack_start(GTK_BOX(vbox2
), paned
, TRUE
, TRUE
, 0);
8179 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8180 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8181 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8182 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8183 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8185 gtk_widget_show_all(paned
);
8188 if (prefs_common
.textfont
) {
8189 PangoFontDescription
*font_desc
;
8191 font_desc
= pango_font_description_from_string
8192 (prefs_common
.textfont
);
8194 gtk_widget_override_font(text
, font_desc
);
8195 pango_font_description_free(font_desc
);
8199 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8200 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8201 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8202 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8203 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8204 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8205 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8206 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8208 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8210 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8211 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8212 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8214 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8216 undostruct
= undo_init(text
);
8217 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8220 address_completion_start(window
);
8222 compose
->window
= window
;
8223 compose
->vbox
= vbox
;
8224 compose
->menubar
= menubar
;
8225 compose
->handlebox
= handlebox
;
8227 compose
->vbox2
= vbox2
;
8229 compose
->paned
= paned
;
8231 compose
->attach_label
= attach_lab2
;
8233 compose
->notebook
= notebook
;
8234 compose
->edit_vbox
= edit_vbox
;
8235 compose
->ruler_hbox
= ruler_hbox
;
8236 compose
->ruler
= ruler
;
8237 compose
->scrolledwin
= scrolledwin
;
8238 compose
->text
= text
;
8240 compose
->focused_editable
= NULL
;
8242 compose
->popupmenu
= popupmenu
;
8244 compose
->tmpl_menu
= tmpl_menu
;
8246 compose
->mode
= mode
;
8247 compose
->rmode
= mode
;
8249 compose
->targetinfo
= NULL
;
8250 compose
->replyinfo
= NULL
;
8251 compose
->fwdinfo
= NULL
;
8253 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8254 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8256 compose
->replyto
= NULL
;
8258 compose
->bcc
= NULL
;
8259 compose
->followup_to
= NULL
;
8261 compose
->ml_post
= NULL
;
8263 compose
->inreplyto
= NULL
;
8264 compose
->references
= NULL
;
8265 compose
->msgid
= NULL
;
8266 compose
->boundary
= NULL
;
8268 compose
->autowrap
= prefs_common
.autowrap
;
8269 compose
->autoindent
= prefs_common
.auto_indent
;
8270 compose
->use_signing
= FALSE
;
8271 compose
->use_encryption
= FALSE
;
8272 compose
->privacy_system
= NULL
;
8273 compose
->encdata
= NULL
;
8275 compose
->modified
= FALSE
;
8277 compose
->return_receipt
= FALSE
;
8279 compose
->to_list
= NULL
;
8280 compose
->newsgroup_list
= NULL
;
8282 compose
->undostruct
= undostruct
;
8284 compose
->sig_str
= NULL
;
8286 compose
->exteditor_file
= NULL
;
8287 compose
->exteditor_pid
= INVALID_PID
;
8288 compose
->exteditor_tag
= -1;
8289 compose
->exteditor_socket
= NULL
;
8290 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8292 compose
->folder_update_callback_id
=
8293 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8294 compose_update_folder_hook
,
8295 (gpointer
) compose
);
8298 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8299 if (mode
!= COMPOSE_REDIRECT
) {
8300 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8301 strcmp(prefs_common
.dictionary
, "")) {
8302 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8303 prefs_common
.alt_dictionary
,
8304 conv_get_locale_charset_str(),
8305 prefs_common
.color
[COL_MISSPELLED
],
8306 prefs_common
.check_while_typing
,
8307 prefs_common
.recheck_when_changing_dict
,
8308 prefs_common
.use_alternate
,
8309 prefs_common
.use_both_dicts
,
8310 GTK_TEXT_VIEW(text
),
8311 GTK_WINDOW(compose
->window
),
8312 compose_dict_changed
,
8313 compose_spell_menu_changed
,
8316 alertpanel_error(_("Spell checker could not "
8318 gtkaspell_checkers_strerror());
8319 gtkaspell_checkers_reset_error();
8321 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8325 compose
->gtkaspell
= gtkaspell
;
8326 compose_spell_menu_changed(compose
);
8327 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8330 compose_select_account(compose
, account
, TRUE
);
8332 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8333 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8335 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8336 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8338 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8339 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8341 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8342 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8344 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8345 if (account
->protocol
!= A_NNTP
)
8346 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8347 prefs_common_translated_header_name("To:"));
8349 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8350 prefs_common_translated_header_name("Newsgroups:"));
8352 #ifndef USE_ALT_ADDRBOOK
8353 addressbook_set_target_compose(compose
);
8355 if (mode
!= COMPOSE_REDIRECT
)
8356 compose_set_template_menu(compose
);
8358 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8361 compose_list
= g_list_append(compose_list
, compose
);
8363 if (!prefs_common
.show_ruler
)
8364 gtk_widget_hide(ruler_hbox
);
8366 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8369 compose
->priority
= PRIORITY_NORMAL
;
8370 compose_update_priority_menu_item(compose
);
8372 compose_set_out_encoding(compose
);
8375 compose_update_actions_menu(compose
);
8377 /* Privacy Systems menu */
8378 compose_update_privacy_systems_menu(compose
);
8379 compose_activate_privacy_system(compose
, account
, TRUE
);
8381 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8383 gtk_widget_realize(window
);
8385 gtk_widget_show(window
);
8391 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8396 GtkWidget
*optmenubox
;
8397 GtkWidget
*fromlabel
;
8400 GtkWidget
*from_name
= NULL
;
8402 gint num
= 0, def_menu
= 0;
8404 accounts
= account_get_list();
8405 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8407 optmenubox
= gtk_event_box_new();
8408 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8409 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8411 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 4);
8412 from_name
= gtk_entry_new();
8414 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8415 G_CALLBACK(compose_grab_focus_cb
), compose
);
8416 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8417 G_CALLBACK(from_name_activate_cb
), optmenu
);
8419 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8420 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8421 gchar
*name
, *from
= NULL
;
8423 if (ac
== compose
->account
) def_menu
= num
;
8425 name
= g_markup_printf_escaped("<i>%s</i>",
8428 if (ac
== compose
->account
) {
8429 if (ac
->name
&& *ac
->name
) {
8431 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8432 from
= g_strdup_printf("%s <%s>",
8434 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8436 from
= g_strdup_printf("%s",
8438 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8440 if (cur_account
!= compose
->account
) {
8443 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor
, color
);
8444 gtk_widget_modify_base(
8445 GTK_WIDGET(from_name
),
8446 GTK_STATE_NORMAL
, &color
);
8447 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color
, color
);
8448 gtk_widget_modify_text(
8449 GTK_WIDGET(from_name
),
8450 GTK_STATE_NORMAL
, &color
);
8453 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8458 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8460 g_signal_connect(G_OBJECT(optmenu
), "changed",
8461 G_CALLBACK(account_activated
),
8463 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8464 G_CALLBACK(compose_entry_popup_extend
),
8467 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8468 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8470 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8471 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8472 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8474 CLAWS_SET_TIP(optmenubox
,
8475 _("Account to use for this email"));
8476 CLAWS_SET_TIP(from_name
,
8477 _("Sender address to be used"));
8479 compose
->account_combo
= optmenu
;
8480 compose
->from_name
= from_name
;
8485 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8487 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8488 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8489 Compose
*compose
= (Compose
*) data
;
8491 compose
->priority
= value
;
8495 static void compose_reply_change_mode(Compose
*compose
,
8498 gboolean was_modified
= compose
->modified
;
8500 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8502 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8504 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8506 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8508 if (action
== COMPOSE_REPLY_TO_ALL
)
8510 if (action
== COMPOSE_REPLY_TO_SENDER
)
8512 if (action
== COMPOSE_REPLY_TO_LIST
)
8515 compose_remove_header_entries(compose
);
8516 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8517 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8518 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8520 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8521 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8523 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8524 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8525 compose_show_first_last_header(compose
, TRUE
);
8526 compose
->modified
= was_modified
;
8527 compose_set_title(compose
);
8530 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8532 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8533 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8534 Compose
*compose
= (Compose
*) data
;
8537 compose_reply_change_mode(compose
, value
);
8540 static void compose_update_priority_menu_item(Compose
* compose
)
8542 GtkWidget
*menuitem
= NULL
;
8543 switch (compose
->priority
) {
8544 case PRIORITY_HIGHEST
:
8545 menuitem
= gtk_ui_manager_get_widget
8546 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8549 menuitem
= gtk_ui_manager_get_widget
8550 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8552 case PRIORITY_NORMAL
:
8553 menuitem
= gtk_ui_manager_get_widget
8554 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8557 menuitem
= gtk_ui_manager_get_widget
8558 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8560 case PRIORITY_LOWEST
:
8561 menuitem
= gtk_ui_manager_get_widget
8562 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8565 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8568 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8570 Compose
*compose
= (Compose
*) data
;
8572 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8574 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8576 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8579 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8580 g_free(compose
->privacy_system
);
8581 compose
->privacy_system
= NULL
;
8582 g_free(compose
->encdata
);
8583 compose
->encdata
= NULL
;
8584 if (systemid
!= NULL
) {
8585 compose
->privacy_system
= g_strdup(systemid
);
8587 can_sign
= privacy_system_can_sign(systemid
);
8588 can_encrypt
= privacy_system_can_encrypt(systemid
);
8591 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8593 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8595 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8596 gtk_widget_set_sensitive(
8597 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8599 gtk_toggle_tool_button_set_active(
8600 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8601 can_sign
? compose
->use_signing
: FALSE
);
8603 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8604 gtk_widget_set_sensitive(
8605 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8607 gtk_toggle_tool_button_set_active(
8608 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8609 can_encrypt
? compose
->use_encryption
: FALSE
);
8613 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8615 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8616 GtkWidget
*menuitem
= NULL
;
8617 GList
*children
, *amenu
;
8618 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8619 gboolean found
= FALSE
;
8621 if (compose
->privacy_system
!= NULL
) {
8623 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8624 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8625 cm_return_if_fail(menuitem
!= NULL
);
8627 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8630 while (amenu
!= NULL
) {
8631 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8632 if (systemid
!= NULL
) {
8633 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8634 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8635 menuitem
= GTK_WIDGET(amenu
->data
);
8637 can_sign
= privacy_system_can_sign(systemid
);
8638 can_encrypt
= privacy_system_can_encrypt(systemid
);
8642 } else if (strlen(compose
->privacy_system
) == 0 &&
8643 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8644 menuitem
= GTK_WIDGET(amenu
->data
);
8647 can_encrypt
= FALSE
;
8652 amenu
= amenu
->next
;
8654 g_list_free(children
);
8655 if (menuitem
!= NULL
)
8656 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8658 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8659 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8660 "will not be able to sign or encrypt this message."),
8661 compose
->privacy_system
);
8665 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8666 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8667 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8668 gtk_widget_set_sensitive(
8669 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8672 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8673 gtk_widget_set_sensitive(
8674 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8679 static void compose_set_out_encoding(Compose
*compose
)
8681 CharSet out_encoding
;
8682 const gchar
*branch
= NULL
;
8683 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8685 switch(out_encoding
) {
8686 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8687 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8688 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8689 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8690 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8691 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8692 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8693 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8694 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8695 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8696 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8697 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8698 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8699 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8700 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8701 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8702 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8703 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8704 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8705 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8706 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8707 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8708 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8709 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8710 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8711 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8712 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8713 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8714 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8715 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8716 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8717 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8718 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8719 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8721 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8724 static void compose_set_template_menu(Compose
*compose
)
8726 GSList
*tmpl_list
, *cur
;
8730 tmpl_list
= template_get_config();
8732 menu
= gtk_menu_new();
8734 gtk_menu_set_accel_group (GTK_MENU (menu
),
8735 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8736 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8737 Template
*tmpl
= (Template
*)cur
->data
;
8738 gchar
*accel_path
= NULL
;
8739 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8740 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8741 g_signal_connect(G_OBJECT(item
), "activate",
8742 G_CALLBACK(compose_template_activate_cb
),
8744 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8745 gtk_widget_show(item
);
8746 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8747 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8751 gtk_widget_show(menu
);
8752 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8755 void compose_update_actions_menu(Compose
*compose
)
8757 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8760 static void compose_update_privacy_systems_menu(Compose
*compose
)
8762 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8763 GSList
*systems
, *cur
;
8765 GtkWidget
*system_none
;
8767 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8768 GtkWidget
*privacy_menu
= gtk_menu_new();
8770 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8771 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8773 g_signal_connect(G_OBJECT(system_none
), "activate",
8774 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8776 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8777 gtk_widget_show(system_none
);
8779 systems
= privacy_get_system_ids();
8780 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8781 gchar
*systemid
= cur
->data
;
8783 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8784 widget
= gtk_radio_menu_item_new_with_label(group
,
8785 privacy_system_get_name(systemid
));
8786 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8787 g_strdup(systemid
), g_free
);
8788 g_signal_connect(G_OBJECT(widget
), "activate",
8789 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8791 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8792 gtk_widget_show(widget
);
8795 g_slist_free(systems
);
8796 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8797 gtk_widget_show_all(privacy_menu
);
8798 gtk_widget_show_all(privacy_menuitem
);
8801 void compose_reflect_prefs_all(void)
8806 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8807 compose
= (Compose
*)cur
->data
;
8808 compose_set_template_menu(compose
);
8812 void compose_reflect_prefs_pixmap_theme(void)
8817 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8818 compose
= (Compose
*)cur
->data
;
8819 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8823 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8825 const gchar
*qmark
= NULL
;
8827 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8829 switch (compose
->mode
) {
8830 /* use forward-specific quote char */
8831 case COMPOSE_FORWARD
:
8832 case COMPOSE_FORWARD_AS_ATTACH
:
8833 case COMPOSE_FORWARD_INLINE
:
8834 if (compose
->folder
&& compose
->folder
->prefs
&&
8835 compose
->folder
->prefs
->forward_with_format
)
8836 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8837 else if (compose
->account
->forward_with_format
)
8838 qmark
= compose
->account
->forward_quotemark
;
8840 qmark
= prefs_common
.fw_quotemark
;
8843 /* use reply-specific quote char in all other modes */
8845 if (compose
->folder
&& compose
->folder
->prefs
&&
8846 compose
->folder
->prefs
->reply_with_format
)
8847 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8848 else if (compose
->account
->reply_with_format
)
8849 qmark
= compose
->account
->reply_quotemark
;
8851 qmark
= prefs_common
.quotemark
;
8855 if (qmark
== NULL
|| *qmark
== '\0')
8861 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8865 GtkTextBuffer
*buffer
;
8869 gchar
*parsed_str
= NULL
;
8870 gint cursor_pos
= 0;
8871 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8874 /* process the body */
8876 text
= GTK_TEXT_VIEW(compose
->text
);
8877 buffer
= gtk_text_view_get_buffer(text
);
8880 qmark
= compose_quote_char_from_context(compose
);
8882 if (compose
->replyinfo
!= NULL
) {
8885 gtk_text_buffer_set_text(buffer
, "", -1);
8886 mark
= gtk_text_buffer_get_insert(buffer
);
8887 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8889 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8890 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8892 } else if (compose
->fwdinfo
!= NULL
) {
8895 gtk_text_buffer_set_text(buffer
, "", -1);
8896 mark
= gtk_text_buffer_get_insert(buffer
);
8897 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8899 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8900 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8903 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8905 GtkTextIter start
, end
;
8908 gtk_text_buffer_get_start_iter(buffer
, &start
);
8909 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8910 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8912 /* clear the buffer now */
8914 gtk_text_buffer_set_text(buffer
, "", -1);
8916 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8917 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8918 procmsg_msginfo_free( &dummyinfo
);
8924 gtk_text_buffer_set_text(buffer
, "", -1);
8925 mark
= gtk_text_buffer_get_insert(buffer
);
8926 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8929 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8930 compose_insert_sig(compose
, FALSE
);
8932 if (replace
&& parsed_str
) {
8933 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8934 gtk_text_buffer_place_cursor(buffer
, &iter
);
8938 cursor_pos
= quote_fmt_get_cursor_pos();
8939 compose
->set_cursor_pos
= cursor_pos
;
8940 if (cursor_pos
== -1)
8942 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8943 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8944 gtk_text_buffer_place_cursor(buffer
, &iter
);
8947 /* process the other fields */
8949 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8950 compose_template_apply_fields(compose
, tmpl
);
8951 quote_fmt_reset_vartable();
8952 quote_fmtlex_destroy();
8954 compose_changed_cb(NULL
, compose
);
8957 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8958 gtkaspell_highlight_all(compose
->gtkaspell
);
8962 static void compose_template_apply_fields_error(const gchar
*header
)
8967 tr
= g_strdup(C_("'%s' stands for a header name",
8968 "Template '%s' format error."));
8969 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8970 alertpanel_error("%s", text
);
8976 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8978 MsgInfo
* dummyinfo
= NULL
;
8979 MsgInfo
*msginfo
= NULL
;
8982 if (compose
->replyinfo
!= NULL
)
8983 msginfo
= compose
->replyinfo
;
8984 else if (compose
->fwdinfo
!= NULL
)
8985 msginfo
= compose
->fwdinfo
;
8987 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8988 msginfo
= dummyinfo
;
8991 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8993 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8994 compose
->gtkaspell
);
8996 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8998 quote_fmt_scan_string(tmpl
->from
);
9001 buf
= quote_fmt_get_buffer();
9003 compose_template_apply_fields_error("From");
9005 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
9008 quote_fmt_reset_vartable();
9009 quote_fmtlex_destroy();
9012 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
9014 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9015 compose
->gtkaspell
);
9017 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9019 quote_fmt_scan_string(tmpl
->to
);
9022 buf
= quote_fmt_get_buffer();
9024 compose_template_apply_fields_error("To");
9026 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9029 quote_fmt_reset_vartable();
9030 quote_fmtlex_destroy();
9033 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9035 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9036 compose
->gtkaspell
);
9038 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9040 quote_fmt_scan_string(tmpl
->cc
);
9043 buf
= quote_fmt_get_buffer();
9045 compose_template_apply_fields_error("Cc");
9047 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9050 quote_fmt_reset_vartable();
9051 quote_fmtlex_destroy();
9054 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9056 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9057 compose
->gtkaspell
);
9059 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9061 quote_fmt_scan_string(tmpl
->bcc
);
9064 buf
= quote_fmt_get_buffer();
9066 compose_template_apply_fields_error("Bcc");
9068 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9071 quote_fmt_reset_vartable();
9072 quote_fmtlex_destroy();
9075 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9077 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9078 compose
->gtkaspell
);
9080 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9082 quote_fmt_scan_string(tmpl
->replyto
);
9085 buf
= quote_fmt_get_buffer();
9087 compose_template_apply_fields_error("Reply-To");
9089 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9092 quote_fmt_reset_vartable();
9093 quote_fmtlex_destroy();
9096 /* process the subject */
9097 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9099 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9100 compose
->gtkaspell
);
9102 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9104 quote_fmt_scan_string(tmpl
->subject
);
9107 buf
= quote_fmt_get_buffer();
9109 compose_template_apply_fields_error("Subject");
9111 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9114 quote_fmt_reset_vartable();
9115 quote_fmtlex_destroy();
9118 procmsg_msginfo_free( &dummyinfo
);
9121 static void compose_destroy(Compose
*compose
)
9123 GtkAllocation allocation
;
9124 GtkTextBuffer
*buffer
;
9125 GtkClipboard
*clipboard
;
9127 compose_list
= g_list_remove(compose_list
, compose
);
9130 gboolean enable
= TRUE
;
9131 g_slist_foreach(compose
->passworded_ldap_servers
,
9132 _ldap_srv_func
, &enable
);
9133 g_slist_free(compose
->passworded_ldap_servers
);
9136 if (compose
->updating
) {
9137 debug_print("danger, not destroying anything now\n");
9138 compose
->deferred_destroy
= TRUE
;
9142 /* NOTE: address_completion_end() does nothing with the window
9143 * however this may change. */
9144 address_completion_end(compose
->window
);
9146 slist_free_strings_full(compose
->to_list
);
9147 slist_free_strings_full(compose
->newsgroup_list
);
9148 slist_free_strings_full(compose
->header_list
);
9150 slist_free_strings_full(extra_headers
);
9151 extra_headers
= NULL
;
9153 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9155 g_hash_table_destroy(compose
->email_hashtable
);
9157 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9158 compose
->folder_update_callback_id
);
9160 procmsg_msginfo_free(&(compose
->targetinfo
));
9161 procmsg_msginfo_free(&(compose
->replyinfo
));
9162 procmsg_msginfo_free(&(compose
->fwdinfo
));
9164 g_free(compose
->replyto
);
9165 g_free(compose
->cc
);
9166 g_free(compose
->bcc
);
9167 g_free(compose
->newsgroups
);
9168 g_free(compose
->followup_to
);
9170 g_free(compose
->ml_post
);
9172 g_free(compose
->inreplyto
);
9173 g_free(compose
->references
);
9174 g_free(compose
->msgid
);
9175 g_free(compose
->boundary
);
9177 g_free(compose
->redirect_filename
);
9178 if (compose
->undostruct
)
9179 undo_destroy(compose
->undostruct
);
9181 g_free(compose
->sig_str
);
9183 g_free(compose
->exteditor_file
);
9185 g_free(compose
->orig_charset
);
9187 g_free(compose
->privacy_system
);
9188 g_free(compose
->encdata
);
9190 #ifndef USE_ALT_ADDRBOOK
9191 if (addressbook_get_target_compose() == compose
)
9192 addressbook_set_target_compose(NULL
);
9195 if (compose
->gtkaspell
) {
9196 gtkaspell_delete(compose
->gtkaspell
);
9197 compose
->gtkaspell
= NULL
;
9201 if (!compose
->batch
) {
9202 gtk_window_get_size(GTK_WINDOW(compose
->window
),
9203 &allocation
.width
, &allocation
.height
);
9204 prefs_common
.compose_width
= allocation
.width
;
9205 prefs_common
.compose_height
= allocation
.height
;
9208 if (!gtk_widget_get_parent(compose
->paned
))
9209 gtk_widget_destroy(compose
->paned
);
9210 gtk_widget_destroy(compose
->popupmenu
);
9212 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9213 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9214 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9216 message_search_close(compose
);
9217 gtk_widget_destroy(compose
->window
);
9218 toolbar_destroy(compose
->toolbar
);
9219 g_free(compose
->toolbar
);
9220 g_mutex_clear(&compose
->mutex
);
9224 static void compose_attach_info_free(AttachInfo
*ainfo
)
9226 g_free(ainfo
->file
);
9227 g_free(ainfo
->content_type
);
9228 g_free(ainfo
->name
);
9229 g_free(ainfo
->charset
);
9233 static void compose_attach_update_label(Compose
*compose
)
9238 GtkTreeModel
*model
;
9242 if (compose
== NULL
)
9245 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9246 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9247 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9251 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9252 total_size
= ainfo
->size
;
9253 while(gtk_tree_model_iter_next(model
, &iter
)) {
9254 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9255 total_size
+= ainfo
->size
;
9258 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9259 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9263 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9265 Compose
*compose
= (Compose
*)data
;
9266 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9267 GtkTreeSelection
*selection
;
9269 GtkTreeModel
*model
;
9271 selection
= gtk_tree_view_get_selection(tree_view
);
9272 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9273 cm_return_if_fail(sel
);
9275 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9276 GtkTreePath
*path
= cur
->data
;
9277 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9280 gtk_tree_path_free(path
);
9283 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9284 GtkTreeRowReference
*ref
= cur
->data
;
9285 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9288 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9289 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9291 gtk_tree_path_free(path
);
9292 gtk_tree_row_reference_free(ref
);
9296 compose_attach_update_label(compose
);
9299 static struct _AttachProperty
9302 GtkWidget
*mimetype_entry
;
9303 GtkWidget
*encoding_optmenu
;
9304 GtkWidget
*path_entry
;
9305 GtkWidget
*filename_entry
;
9307 GtkWidget
*cancel_btn
;
9310 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9312 gtk_tree_path_free((GtkTreePath
*)ptr
);
9315 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9317 Compose
*compose
= (Compose
*)data
;
9318 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9320 GtkComboBox
*optmenu
;
9321 GtkTreeSelection
*selection
;
9323 GtkTreeModel
*model
;
9326 static gboolean cancelled
;
9328 /* only if one selected */
9329 selection
= gtk_tree_view_get_selection(tree_view
);
9330 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9333 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9334 cm_return_if_fail(sel
);
9336 path
= (GtkTreePath
*) sel
->data
;
9337 gtk_tree_model_get_iter(model
, &iter
, path
);
9338 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9341 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9347 if (!attach_prop
.window
)
9348 compose_attach_property_create(&cancelled
);
9349 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9350 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9351 gtk_widget_show(attach_prop
.window
);
9352 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9353 GTK_WINDOW(compose
->window
));
9355 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9356 if (ainfo
->encoding
== ENC_UNKNOWN
)
9357 combobox_select_by_data(optmenu
, ENC_BASE64
);
9359 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9361 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9362 ainfo
->content_type
? ainfo
->content_type
: "");
9363 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9364 ainfo
->file
? ainfo
->file
: "");
9365 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9366 ainfo
->name
? ainfo
->name
: "");
9369 const gchar
*entry_text
;
9371 gchar
*cnttype
= NULL
;
9378 gtk_widget_hide(attach_prop
.window
);
9379 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9384 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9385 if (*entry_text
!= '\0') {
9388 text
= g_strstrip(g_strdup(entry_text
));
9389 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9390 cnttype
= g_strdup(text
);
9393 alertpanel_error(_("Invalid MIME type."));
9399 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9401 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9402 if (*entry_text
!= '\0') {
9403 if (is_file_exist(entry_text
) &&
9404 (size
= get_file_size(entry_text
)) > 0)
9405 file
= g_strdup(entry_text
);
9408 (_("File doesn't exist or is empty."));
9414 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9415 if (*entry_text
!= '\0') {
9416 g_free(ainfo
->name
);
9417 ainfo
->name
= g_strdup(entry_text
);
9421 g_free(ainfo
->content_type
);
9422 ainfo
->content_type
= cnttype
;
9425 g_free(ainfo
->file
);
9429 ainfo
->size
= (goffset
)size
;
9431 /* update tree store */
9432 text
= to_human_readable(ainfo
->size
);
9433 gtk_tree_model_get_iter(model
, &iter
, path
);
9434 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9435 COL_MIMETYPE
, ainfo
->content_type
,
9437 COL_NAME
, ainfo
->name
,
9438 COL_CHARSET
, ainfo
->charset
,
9444 gtk_tree_path_free(path
);
9447 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9449 label = gtk_label_new(str); \
9450 gtk_grid_attach(GTK_GRID(table), label, 0, top, 1, 1); \
9451 gtk_label_set_xalign(GTK_LABEL(label), 0.0); \
9452 entry = gtk_entry_new(); \
9453 gtk_grid_attach(GTK_GRID(table), entry, 1, top, 1, 1); \
9456 static void compose_attach_property_create(gboolean
*cancelled
)
9462 GtkWidget
*mimetype_entry
;
9465 GtkListStore
*optmenu_menu
;
9466 GtkWidget
*path_entry
;
9467 GtkWidget
*filename_entry
;
9470 GtkWidget
*cancel_btn
;
9471 GList
*mime_type_list
, *strlist
;
9474 debug_print("Creating attach_property window...\n");
9476 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9477 gtk_widget_set_size_request(window
, 480, -1);
9478 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9479 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9480 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9481 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9482 g_signal_connect(G_OBJECT(window
), "delete_event",
9483 G_CALLBACK(attach_property_delete_event
),
9485 g_signal_connect(G_OBJECT(window
), "key_press_event",
9486 G_CALLBACK(attach_property_key_pressed
),
9489 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
9490 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9492 table
= gtk_grid_new();
9493 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9494 gtk_grid_set_row_spacing(GTK_GRID(table
), 8);
9495 gtk_grid_set_column_spacing(GTK_GRID(table
), 8);
9497 label
= gtk_label_new(_("MIME type"));
9498 gtk_grid_attach(GTK_GRID(table
), label
, 0, 0, 1, 1);
9499 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9500 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9501 gtk_grid_attach(GTK_GRID(table
), mimetype_entry
, 1, 0, 1, 1);
9502 gtk_widget_set_hexpand(mimetype_entry
, TRUE
);
9503 gtk_widget_set_halign(mimetype_entry
, GTK_ALIGN_FILL
);
9505 /* stuff with list */
9506 mime_type_list
= procmime_get_mime_type_list();
9508 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9509 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9512 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9514 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9517 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9518 (GCompareFunc
)g_strcmp0
);
9521 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9522 mime_type_list
= mime_type_list
->next
) {
9523 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9524 g_free(mime_type_list
->data
);
9526 g_list_free(strlist
);
9527 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9528 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9530 label
= gtk_label_new(_("Encoding"));
9531 gtk_grid_attach(GTK_GRID(table
), label
, 0, 1, 1, 1);
9532 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9534 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
9535 gtk_grid_attach(GTK_GRID(table
), hbox
, 1, 1, 1, 1);
9536 gtk_widget_set_hexpand(hbox
, TRUE
);
9537 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
9539 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9540 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9542 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9543 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9544 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9545 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9546 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9548 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9550 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9551 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9553 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, NULL
, _("_Cancel"),
9554 &ok_btn
, NULL
, _("_OK"),
9556 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9557 gtk_widget_grab_default(ok_btn
);
9559 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9560 G_CALLBACK(attach_property_ok
),
9562 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9563 G_CALLBACK(attach_property_cancel
),
9566 gtk_widget_show_all(vbox
);
9568 attach_prop
.window
= window
;
9569 attach_prop
.mimetype_entry
= mimetype_entry
;
9570 attach_prop
.encoding_optmenu
= optmenu
;
9571 attach_prop
.path_entry
= path_entry
;
9572 attach_prop
.filename_entry
= filename_entry
;
9573 attach_prop
.ok_btn
= ok_btn
;
9574 attach_prop
.cancel_btn
= cancel_btn
;
9577 #undef SET_LABEL_AND_ENTRY
9579 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9585 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9591 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9592 gboolean
*cancelled
)
9600 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9602 gboolean
*cancelled
)
9604 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9608 if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
||
9609 event
->keyval
== GDK_KEY_Return
)) {
9617 static gboolean
compose_can_autosave(Compose
*compose
)
9619 if (compose
->privacy_system
&& compose
->use_encryption
)
9620 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9622 return prefs_common
.autosave
;
9626 * compose_exec_ext_editor:
9628 * Open (and optionally embed) external editor
9630 static void compose_exec_ext_editor(Compose
*compose
)
9635 Window socket_wid
= 0;
9637 #endif /* G_OS_WIN32 */
9639 GError
*error
= NULL
;
9643 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9644 G_DIR_SEPARATOR
, compose
);
9646 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9647 alertpanel_error(_("Could not write the body to file:\n%s"),
9653 if (compose_get_ext_editor_uses_socket()) {
9655 /* Only allow one socket */
9656 if (compose
->exteditor_socket
!= NULL
) {
9657 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9658 /* Move the focus off of the socket */
9659 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9664 /* Create the receiving GtkSocket */
9665 socket
= gtk_socket_new ();
9666 g_signal_connect (G_OBJECT(socket
), "plug-removed",
9667 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9669 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9670 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9671 /* Realize the socket so that we can use its ID */
9672 gtk_widget_realize(socket
);
9673 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9674 compose
->exteditor_socket
= socket
;
9676 alertpanel_error(_("Socket communication with an external editor is not available on Windows."));
9679 #endif /* G_OS_WIN32 */
9682 if (compose_get_ext_editor_cmd_valid()) {
9683 if (compose_get_ext_editor_uses_socket()) {
9685 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9686 s
= strstr(p
, "%w");
9688 if (strstr(p
, "%s") < s
)
9689 cmd
= g_strdup_printf(p
, tmp
, socket_wid
);
9691 cmd
= g_strdup_printf(p
, socket_wid
, tmp
);
9693 #endif /* G_OS_WIN32 */
9695 cmd
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), tmp
);
9698 if (prefs_common_get_ext_editor_cmd())
9699 g_warning("external editor command-line is invalid: '%s'",
9700 prefs_common_get_ext_editor_cmd());
9701 cmd
= g_strdup_printf(DEFAULT_EDITOR_CMD
, tmp
);
9704 argv
= strsplit_with_quote(cmd
, " ", 0);
9706 if (!g_spawn_async(NULL
, argv
, NULL
,
9707 G_SPAWN_DO_NOT_REAP_CHILD
| G_SPAWN_SEARCH_PATH
,
9708 NULL
, NULL
, &pid
, &error
)) {
9709 alertpanel_error(_("Could not spawn the following "
9710 "external editor command:\n%s\n%s"),
9711 cmd
, error
? error
->message
: _("Unknown error"));
9713 g_error_free(error
);
9722 compose
->exteditor_file
= g_strdup(tmp
);
9723 compose
->exteditor_pid
= pid
;
9724 compose
->exteditor_tag
= g_child_watch_add(pid
,
9725 compose_ext_editor_closed_cb
,
9728 compose_set_ext_editor_sensitive(compose
, FALSE
);
9734 * compose_ext_editor_cb:
9736 * External editor has closed (called by g_child_watch)
9738 static void compose_ext_editor_closed_cb(GPid pid
, gint exit_status
, gpointer data
)
9740 Compose
*compose
= (Compose
*)data
;
9741 GError
*error
= NULL
;
9742 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9743 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9744 GtkTextIter start
, end
;
9747 #if GLIB_CHECK_VERSION(2,70,0)
9748 if (!g_spawn_check_wait_status(exit_status
, &error
)) {
9750 if (!g_spawn_check_exit_status(exit_status
, &error
)) {
9753 _("External editor stopped with an error: %s"),
9754 error
? error
->message
: _("Unknown error"));
9756 g_error_free(error
);
9758 g_spawn_close_pid(compose
->exteditor_pid
);
9760 gtk_text_buffer_set_text(buffer
, "", -1);
9761 compose_insert_file(compose
, compose
->exteditor_file
);
9762 compose_changed_cb(NULL
, compose
);
9764 /* Check if we should save the draft or not */
9765 if (compose_can_autosave(compose
))
9766 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9768 if (claws_unlink(compose
->exteditor_file
) < 0)
9769 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9771 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9772 gtk_text_buffer_get_start_iter(buffer
, &start
);
9773 gtk_text_buffer_get_end_iter(buffer
, &end
);
9774 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9775 if (chars
&& strlen(chars
) > 0)
9776 compose
->modified
= TRUE
;
9779 compose_set_ext_editor_sensitive(compose
, TRUE
);
9781 g_free(compose
->exteditor_file
);
9782 compose
->exteditor_file
= NULL
;
9783 compose
->exteditor_pid
= INVALID_PID
;
9784 compose
->exteditor_tag
= -1;
9785 if (compose
->exteditor_socket
) {
9786 gtk_widget_destroy(compose
->exteditor_socket
);
9787 compose
->exteditor_socket
= NULL
;
9792 static gboolean
compose_get_ext_editor_cmd_valid()
9794 gboolean has_s
= FALSE
;
9795 gboolean has_w
= FALSE
;
9796 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9799 while ((p
= strchr(p
, '%'))) {
9805 } else if (*p
== 'w') {
9816 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9818 GPid pid
= compose
->exteditor_pid
;
9819 gchar
*pidmsg
= NULL
;
9825 pidmsg
= g_strdup_printf(_("process id: %" G_PID_FORMAT
), pid
);
9827 msg
= g_strdup_printf
9828 (_("The external editor is still working.\n"
9829 "Force terminating the process?\n"
9831 val
= alertpanel_full(_("Notice"), msg
, NULL
, _("_No"), NULL
, _("_Yes"),
9832 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9836 if (val
== G_ALERTALTERNATE
) {
9837 g_source_remove(compose
->exteditor_tag
);
9840 if (!TerminateProcess(compose
->exteditor_pid
, 0))
9841 perror("TerminateProcess");
9843 if (kill(pid
, SIGTERM
) < 0) perror("kill");
9844 waitpid(compose
->exteditor_pid
, NULL
, 0);
9845 #endif /* G_OS_WIN32 */
9847 g_warning("terminated %s, temporary file: %s",
9848 pidmsg
, compose
->exteditor_file
);
9849 g_spawn_close_pid(compose
->exteditor_pid
);
9851 compose_set_ext_editor_sensitive(compose
, TRUE
);
9853 g_free(compose
->exteditor_file
);
9854 compose
->exteditor_file
= NULL
;
9855 compose
->exteditor_pid
= INVALID_PID
;
9856 compose
->exteditor_tag
= -1;
9868 static char *ext_editor_menu_entries
[] = {
9869 "Menu/Message/Send",
9870 "Menu/Message/SendLater",
9871 "Menu/Message/InsertFile",
9872 "Menu/Message/InsertSig",
9873 "Menu/Message/ReplaceSig",
9874 "Menu/Message/Save",
9875 "Menu/Message/Print",
9880 "Menu/Tools/ShowRuler",
9881 "Menu/Tools/Actions",
9886 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9891 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9892 cm_menu_set_sensitive_full(compose
->ui_manager
,
9893 ext_editor_menu_entries
[i
], sensitive
);
9896 if (compose_get_ext_editor_uses_socket()) {
9898 if (compose
->exteditor_socket
)
9899 gtk_widget_hide(compose
->exteditor_socket
);
9900 gtk_widget_show(compose
->scrolledwin
);
9901 if (prefs_common
.show_ruler
)
9902 gtk_widget_show(compose
->ruler_hbox
);
9903 /* Fix the focus, as it doesn't go anywhere when the
9904 * socket is hidden or destroyed */
9905 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9907 g_assert (compose
->exteditor_socket
!= NULL
);
9908 /* Fix the focus, as it doesn't go anywhere when the
9909 * edit box is hidden */
9910 if (gtk_widget_is_focus(compose
->text
))
9911 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9912 gtk_widget_hide(compose
->scrolledwin
);
9913 gtk_widget_hide(compose
->ruler_hbox
);
9914 gtk_widget_show(compose
->exteditor_socket
);
9917 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9919 if (compose
->toolbar
->send_btn
)
9920 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9921 if (compose
->toolbar
->sendl_btn
)
9922 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9923 if (compose
->toolbar
->draft_btn
)
9924 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9925 if (compose
->toolbar
->insert_btn
)
9926 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9927 if (compose
->toolbar
->sig_btn
)
9928 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9929 if (compose
->toolbar
->exteditor_btn
)
9930 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9931 if (compose
->toolbar
->linewrap_current_btn
)
9932 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9933 if (compose
->toolbar
->linewrap_all_btn
)
9934 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9937 static gboolean
compose_get_ext_editor_uses_socket()
9939 return (prefs_common_get_ext_editor_cmd() &&
9940 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9944 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9946 compose
->exteditor_socket
= NULL
;
9947 /* returning FALSE allows destruction of the socket */
9950 #endif /* G_OS_WIN32 */
9953 * compose_undo_state_changed:
9955 * Change the sensivity of the menuentries undo and redo
9957 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9958 gint redo_state
, gpointer data
)
9960 Compose
*compose
= (Compose
*)data
;
9962 switch (undo_state
) {
9963 case UNDO_STATE_TRUE
:
9964 if (!undostruct
->undo_state
) {
9965 undostruct
->undo_state
= TRUE
;
9966 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9969 case UNDO_STATE_FALSE
:
9970 if (undostruct
->undo_state
) {
9971 undostruct
->undo_state
= FALSE
;
9972 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9975 case UNDO_STATE_UNCHANGED
:
9977 case UNDO_STATE_REFRESH
:
9978 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9981 g_warning("undo state not recognized");
9985 switch (redo_state
) {
9986 case UNDO_STATE_TRUE
:
9987 if (!undostruct
->redo_state
) {
9988 undostruct
->redo_state
= TRUE
;
9989 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9992 case UNDO_STATE_FALSE
:
9993 if (undostruct
->redo_state
) {
9994 undostruct
->redo_state
= FALSE
;
9995 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
9998 case UNDO_STATE_UNCHANGED
:
10000 case UNDO_STATE_REFRESH
:
10001 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10004 g_warning("redo state not recognized");
10009 /* callback functions */
10011 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10012 GtkAllocation
*allocation
,
10015 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10018 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10019 * includes "non-client" (windows-izm) in calculation, so this calculation
10020 * may not be accurate.
10022 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10023 GtkAllocation
*allocation
,
10024 GtkSHRuler
*shruler
)
10026 if (prefs_common
.show_ruler
) {
10027 gint char_width
= 0, char_height
= 0;
10028 gint line_width_in_chars
;
10030 gtkut_get_font_size(GTK_WIDGET(widget
),
10031 &char_width
, &char_height
);
10032 line_width_in_chars
=
10033 (allocation
->width
- allocation
->x
) / char_width
;
10035 /* got the maximum */
10036 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10037 0.0, line_width_in_chars
, 0);
10046 ComposePrefType type
;
10047 gboolean entry_marked
;
10048 } HeaderEntryState
;
10050 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10052 Compose
*compose
= (Compose
*)data
;
10055 gchar
*folderidentifier
;
10056 gint account_id
= 0;
10057 GtkTreeModel
*menu
;
10059 GSList
*list
, *saved_list
= NULL
;
10060 HeaderEntryState
*state
;
10062 /* Get ID of active account in the combo box */
10063 menu
= gtk_combo_box_get_model(optmenu
);
10064 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10065 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10067 ac
= account_find_from_id(account_id
);
10068 cm_return_if_fail(ac
!= NULL
);
10070 if (ac
!= compose
->account
) {
10071 compose_select_account(compose
, ac
, FALSE
);
10073 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10074 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10076 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10077 compose_destroy_headerentry(compose
, hentry
);
10080 state
= g_malloc0(sizeof(HeaderEntryState
));
10081 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10082 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10083 state
->entry
= gtk_editable_get_chars(
10084 GTK_EDITABLE(hentry
->entry
), 0, -1);
10085 state
->type
= hentry
->type
;
10087 saved_list
= g_slist_append(saved_list
, state
);
10088 compose_destroy_headerentry(compose
, hentry
);
10091 compose
->header_last
= NULL
;
10092 g_slist_free(compose
->header_list
);
10093 compose
->header_list
= NULL
;
10094 compose
->header_nextrow
= 1;
10095 compose_create_header_entry(compose
);
10097 if (ac
->set_autocc
&& ac
->auto_cc
)
10098 compose_entry_append(compose
, ac
->auto_cc
,
10099 COMPOSE_CC
, PREF_ACCOUNT
);
10100 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10101 compose_entry_append(compose
, ac
->auto_bcc
,
10102 COMPOSE_BCC
, PREF_ACCOUNT
);
10103 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10104 compose_entry_append(compose
, ac
->auto_replyto
,
10105 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10107 for (list
= saved_list
; list
; list
= list
->next
) {
10108 state
= (HeaderEntryState
*) list
->data
;
10110 compose_add_header_entry(compose
, state
->header
,
10111 state
->entry
, state
->type
);
10113 g_free(state
->header
);
10114 g_free(state
->entry
);
10117 g_slist_free(saved_list
);
10119 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10120 (ac
->protocol
== A_NNTP
) ?
10121 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10124 /* Set message save folder */
10125 compose_set_save_to(compose
, NULL
);
10126 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10127 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10128 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10129 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10130 compose_set_save_to(compose
, folderidentifier
);
10131 g_free(folderidentifier
);
10132 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10133 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10134 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10136 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10137 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10138 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10139 (compose
->account
, F_OUTBOX
));
10140 compose_set_save_to(compose
, folderidentifier
);
10141 g_free(folderidentifier
);
10145 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10146 GtkTreeViewColumn
*column
, Compose
*compose
)
10148 compose_attach_property(NULL
, compose
);
10151 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10154 Compose
*compose
= (Compose
*)data
;
10155 GtkTreeSelection
*attach_selection
;
10156 gint attach_nr_selected
;
10159 if (!event
) return FALSE
;
10161 if (event
->button
== 3) {
10162 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10163 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10165 /* If no rows, or just one row is selected, right-click should
10166 * open menu relevant to the row being right-clicked on. We
10167 * achieve that by selecting the clicked row first. If more
10168 * than one row is selected, we shouldn't modify the selection,
10169 * as user may want to remove selected rows (attachments). */
10170 if (attach_nr_selected
< 2) {
10171 gtk_tree_selection_unselect_all(attach_selection
);
10172 attach_nr_selected
= 0;
10173 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10174 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10175 if (path
!= NULL
) {
10176 gtk_tree_selection_select_path(attach_selection
, path
);
10177 gtk_tree_path_free(path
);
10178 attach_nr_selected
++;
10182 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10183 /* Properties menu item makes no sense with more than one row
10184 * selected, the properties dialog can only edit one attachment. */
10185 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10187 gtk_menu_popup_at_pointer(GTK_MENU(compose
->popupmenu
), NULL
);
10195 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10198 Compose
*compose
= (Compose
*)data
;
10200 if (!event
) return FALSE
;
10202 switch (event
->keyval
) {
10203 case GDK_KEY_Delete
:
10204 compose_attach_remove_selected(NULL
, compose
);
10210 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10212 toolbar_comp_set_sensitive(compose
, allow
);
10213 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10214 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10216 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10218 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10219 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10220 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10222 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10226 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10228 Compose
*compose
= (Compose
*)data
;
10231 if (compose
->exteditor_tag
!= -1) {
10232 debug_print("ignoring send: external editor still open\n");
10236 if (prefs_common
.work_offline
&&
10237 !inc_offline_should_override(TRUE
,
10238 _("Claws Mail needs network access in order "
10239 "to send this email.")))
10242 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10243 g_source_remove(compose
->draft_timeout_tag
);
10244 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10247 compose_send(compose
);
10250 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10252 Compose
*compose
= (Compose
*)data
;
10253 ComposeQueueResult val
;
10256 compose_allow_user_actions(compose
, FALSE
);
10257 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10258 compose_allow_user_actions(compose
, TRUE
);
10261 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10262 compose_close(compose
);
10264 _display_queue_error(val
);
10267 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10270 #define DRAFTED_AT_EXIT "drafted_at_exit"
10271 static void compose_register_draft(MsgInfo
*info
)
10273 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10274 DRAFTED_AT_EXIT
, NULL
);
10275 FILE *fp
= claws_fopen(filepath
, "ab");
10278 gchar
*name
= folder_item_get_identifier(info
->folder
);
10279 fprintf(fp
, "%s\t%d\n", name
, info
->msgnum
);
10287 gboolean
compose_draft (gpointer data
, guint action
)
10289 Compose
*compose
= (Compose
*)data
;
10291 FolderItemPrefs
*prefs
;
10295 MsgFlags flag
= {0, 0};
10296 static gboolean lock
= FALSE
;
10297 MsgInfo
*newmsginfo
;
10299 gboolean target_locked
= FALSE
;
10300 gboolean err
= FALSE
;
10303 if (lock
) return FALSE
;
10305 if (compose
->sending
)
10308 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10309 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10311 if (!g_mutex_trylock(&compose
->mutex
)) {
10312 /* we don't want to lock the mutex once it's available,
10313 * because as the only other part of compose.c locking
10314 * it is compose_close - which means once unlocked,
10315 * the compose struct will be freed */
10316 debug_print("couldn't lock mutex, probably sending\n");
10322 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10323 G_DIR_SEPARATOR
, compose
);
10324 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10325 FILE_OP_ERROR(tmp
, "claws_fopen");
10329 /* chmod for security unless folder chmod is set */
10330 prefs
= draft
->prefs
;
10331 if (prefs
&& prefs
->enable_folder_chmod
&& prefs
->folder_chmod
) {
10332 filemode
= prefs
->folder_chmod
;
10333 if (filemode
& S_IRGRP
) filemode
|= S_IWGRP
;
10334 if (filemode
& S_IROTH
) filemode
|= S_IWOTH
;
10335 if (chmod(tmp
, filemode
) < 0)
10336 FILE_OP_ERROR(tmp
, "chmod");
10337 } else if (change_file_mode_rw(fp
, tmp
) < 0) {
10338 FILE_OP_ERROR(tmp
, "chmod");
10339 g_warning("can't change file mode");
10342 /* Save draft infos */
10343 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10344 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10346 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10347 gchar
*savefolderid
;
10349 savefolderid
= compose_get_save_to(compose
);
10350 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10351 g_free(savefolderid
);
10353 if (compose
->return_receipt
) {
10354 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10356 if (compose
->privacy_system
) {
10357 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10358 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10359 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10362 /* Message-ID of message replying to */
10363 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10364 gchar
*folderid
= NULL
;
10366 if (compose
->replyinfo
->folder
)
10367 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10368 if (folderid
== NULL
)
10369 folderid
= g_strdup("NULL");
10371 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10374 /* Message-ID of message forwarding to */
10375 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10376 gchar
*folderid
= NULL
;
10378 if (compose
->fwdinfo
->folder
)
10379 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10380 if (folderid
== NULL
)
10381 folderid
= g_strdup("NULL");
10383 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10387 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10388 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10390 sheaders
= compose_get_manual_headers_info(compose
);
10391 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10394 /* end of headers */
10395 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10402 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10406 if (claws_safe_fclose(fp
) == EOF
) {
10410 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10411 if (compose
->targetinfo
) {
10412 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10414 flag
.perm_flags
|= MSG_LOCKED
;
10416 flag
.tmp_flags
= MSG_DRAFT
;
10418 folder_item_scan(draft
);
10419 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10420 MsgInfo
*tmpinfo
= NULL
;
10421 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10422 if (compose
->msgid
) {
10423 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10426 msgnum
= tmpinfo
->msgnum
;
10427 procmsg_msginfo_free(&tmpinfo
);
10428 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10430 debug_print("didn't get draft msgnum after scanning\n");
10433 debug_print("got draft msgnum %d from adding\n", msgnum
);
10439 if (action
!= COMPOSE_AUTO_SAVE
) {
10440 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10441 alertpanel_error(_("Could not save draft."));
10444 gtkut_window_popup(compose
->window
);
10445 val
= alertpanel_full(_("Could not save draft"),
10446 _("Could not save draft.\n"
10447 "Do you want to cancel exit or discard this email?"),
10448 NULL
, _("_Cancel exit"), NULL
, _("_Discard email"),
10449 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_QUESTION
);
10450 if (val
== G_ALERTALTERNATE
) {
10452 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10453 compose_close(compose
);
10457 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10466 if (compose
->mode
== COMPOSE_REEDIT
) {
10467 compose_remove_reedit_target(compose
, TRUE
);
10470 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10473 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10475 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10477 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10478 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10479 procmsg_msginfo_set_flags(newmsginfo
, 0,
10480 MSG_HAS_ATTACHMENT
);
10482 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10483 compose_register_draft(newmsginfo
);
10485 procmsg_msginfo_free(&newmsginfo
);
10488 folder_item_scan(draft
);
10490 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10492 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10493 compose_close(compose
);
10500 GError
*error
= NULL
;
10505 goffset size
, mtime
;
10507 path
= folder_item_fetch_msg(draft
, msgnum
);
10508 if (path
== NULL
) {
10509 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10513 f
= g_file_new_for_path(path
);
10514 fi
= g_file_query_info(f
, "standard::size,time::modified",
10515 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10516 if (error
!= NULL
) {
10517 debug_print("couldn't query file info for '%s': %s\n",
10518 path
, error
->message
);
10519 g_error_free(error
);
10524 size
= g_file_info_get_size(fi
);
10525 g_file_info_get_modification_time(fi
, &tv
);
10527 g_object_unref(fi
);
10530 if (g_stat(path
, &s
) < 0) {
10531 FILE_OP_ERROR(path
, "stat");
10536 mtime
= s
.st_mtime
;
10540 procmsg_msginfo_free(&(compose
->targetinfo
));
10541 compose
->targetinfo
= procmsg_msginfo_new();
10542 compose
->targetinfo
->msgnum
= msgnum
;
10543 compose
->targetinfo
->size
= size
;
10544 compose
->targetinfo
->mtime
= mtime
;
10545 compose
->targetinfo
->folder
= draft
;
10547 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10548 compose
->mode
= COMPOSE_REEDIT
;
10550 if (action
== COMPOSE_AUTO_SAVE
) {
10551 compose
->modified
= FALSE
;
10552 compose
->autosaved_draft
= compose
->targetinfo
;
10554 compose_set_title(compose
);
10558 g_mutex_unlock(&compose
->mutex
);
10562 void compose_clear_exit_drafts(void)
10564 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10565 DRAFTED_AT_EXIT
, NULL
);
10566 if (is_file_exist(filepath
))
10567 claws_unlink(filepath
);
10572 void compose_reopen_exit_drafts(void)
10574 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10575 DRAFTED_AT_EXIT
, NULL
);
10576 FILE *fp
= claws_fopen(filepath
, "rb");
10580 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10581 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10582 const gchar
*folder
= parts
[0];
10583 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10585 if (folder
&& *folder
&& msgnum
> -1) {
10586 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10587 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10589 compose_reedit(info
, FALSE
);
10596 compose_clear_exit_drafts();
10599 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10601 Compose
*compose
= (Compose
*)data
;
10602 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10603 compose
->rmode
= COMPOSE_REEDIT
;
10604 compose
->modified
= FALSE
;
10605 compose_set_title(compose
);
10608 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10610 if (compose
&& file_list
) {
10613 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10614 gchar
*file
= (gchar
*) tmp
->data
;
10615 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10616 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10617 compose_changed_cb(NULL
, compose
);
10622 g_free(utf8_filename
);
10627 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10629 Compose
*compose
= (Compose
*)data
;
10632 if (compose
->redirect_filename
!= NULL
)
10635 /* Set focus_window properly, in case we were called via popup menu,
10636 * which unsets it (via focus_out_event callback on compose window). */
10637 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10639 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10642 compose_attach_from_list(compose
, file_list
, TRUE
);
10643 g_list_free(file_list
);
10647 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10649 Compose
*compose
= (Compose
*)data
;
10651 gint files_inserted
= 0;
10653 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10658 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10659 gchar
*file
= (gchar
*) tmp
->data
;
10660 gchar
*filedup
= g_strdup(file
);
10661 gchar
*shortfile
= g_path_get_basename(filedup
);
10662 ComposeInsertResult res
;
10663 /* insert the file if the file is short or if the user confirmed that
10664 he/she wants to insert the large file */
10665 res
= compose_insert_file(compose
, file
);
10666 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10667 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10668 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10669 alertpanel_error(_("File '%s' contained invalid characters\n"
10670 "for the current encoding, insertion may be incorrect."),
10672 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10679 g_list_free(file_list
);
10683 if (files_inserted
> 0 && compose
->gtkaspell
&&
10684 compose
->gtkaspell
->check_while_typing
)
10685 gtkaspell_highlight_all(compose
->gtkaspell
);
10689 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10691 Compose
*compose
= (Compose
*)data
;
10693 compose_insert_sig(compose
, FALSE
);
10696 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10698 Compose
*compose
= (Compose
*)data
;
10700 compose_insert_sig(compose
, TRUE
);
10703 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10707 Compose
*compose
= (Compose
*)data
;
10709 gtk_window_get_position(GTK_WINDOW(widget
), &x
, &y
);
10710 if (!compose
->batch
) {
10711 prefs_common
.compose_x
= x
;
10712 prefs_common
.compose_y
= y
;
10714 if (compose
->sending
|| compose
->updating
)
10716 compose_close_cb(NULL
, compose
);
10720 void compose_close_toolbar(Compose
*compose
)
10722 compose_close_cb(NULL
, compose
);
10725 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10727 Compose
*compose
= (Compose
*)data
;
10730 if (compose
->exteditor_tag
!= -1) {
10731 if (!compose_ext_editor_kill(compose
))
10735 if (compose
->modified
) {
10736 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10737 if (!g_mutex_trylock(&compose
->mutex
)) {
10738 /* we don't want to lock the mutex once it's available,
10739 * because as the only other part of compose.c locking
10740 * it is compose_close - which means once unlocked,
10741 * the compose struct will be freed */
10742 debug_print("couldn't lock mutex, probably sending\n");
10745 if (!reedit
|| (compose
->folder
!= NULL
&& compose
->folder
->stype
== F_DRAFT
)) {
10746 val
= alertpanel(_("Discard message"),
10747 _("This message has been modified. Discard it?"),
10748 NULL
, _("_Discard"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10751 val
= alertpanel(_("Save changes"),
10752 _("This message has been modified. Save the latest changes?"),
10753 NULL
, _("_Don't save"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10754 ALERTFOCUS_SECOND
);
10756 g_mutex_unlock(&compose
->mutex
);
10758 case G_ALERTDEFAULT
:
10759 if (compose_can_autosave(compose
) && !reedit
)
10760 compose_remove_draft(compose
);
10762 case G_ALERTALTERNATE
:
10763 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10770 compose_close(compose
);
10773 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10775 Compose
*compose
= (Compose
*) data
;
10777 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10778 if (compose
->targetinfo
)
10779 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10782 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10784 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10785 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10786 Compose
*compose
= (Compose
*) data
;
10789 compose
->out_encoding
= (CharSet
)value
;
10792 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10794 Compose
*compose
= (Compose
*)data
;
10796 #ifndef USE_ALT_ADDRBOOK
10797 addressbook_open(compose
);
10799 GError
* error
= NULL
;
10800 addressbook_connect_signals(compose
);
10801 addressbook_dbus_open(TRUE
, &error
);
10803 g_warning("%s", error
->message
);
10804 g_error_free(error
);
10809 static void about_show_cb(GtkAction
*action
, gpointer data
)
10814 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10816 Compose
*compose
= (Compose
*)data
;
10821 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10822 cm_return_if_fail(tmpl
!= NULL
);
10824 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10826 val
= alertpanel(_("Apply template"), msg
,
10827 NULL
, _("_Replace"), NULL
, _("_Insert"), NULL
, _("_Cancel"),
10831 if (val
== G_ALERTDEFAULT
)
10832 compose_template_apply(compose
, tmpl
, TRUE
);
10833 else if (val
== G_ALERTALTERNATE
)
10834 compose_template_apply(compose
, tmpl
, FALSE
);
10837 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10839 Compose
*compose
= (Compose
*)data
;
10841 if (compose
->exteditor_tag
!= -1) {
10842 debug_print("ignoring open external editor: external editor still open\n");
10845 compose_exec_ext_editor(compose
);
10848 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10850 Compose
*compose
= (Compose
*)data
;
10851 gboolean prev_autowrap
= compose
->autowrap
;
10853 compose
->autowrap
= FALSE
;
10854 undo_undo(compose
->undostruct
);
10855 compose
->autowrap
= prev_autowrap
;
10858 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10860 Compose
*compose
= (Compose
*)data
;
10861 gboolean prev_autowrap
= compose
->autowrap
;
10863 compose
->autowrap
= FALSE
;
10864 undo_redo(compose
->undostruct
);
10865 compose
->autowrap
= prev_autowrap
;
10868 static void entry_cut_clipboard(GtkWidget
*entry
)
10870 if (GTK_IS_EDITABLE(entry
))
10871 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10872 else if (GTK_IS_TEXT_VIEW(entry
))
10873 gtk_text_buffer_cut_clipboard(
10874 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10875 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10879 static void entry_copy_clipboard(GtkWidget
*entry
)
10881 if (GTK_IS_EDITABLE(entry
))
10882 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10883 else if (GTK_IS_TEXT_VIEW(entry
))
10884 gtk_text_buffer_copy_clipboard(
10885 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10886 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10889 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10890 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10892 if (GTK_IS_TEXT_VIEW(entry
)) {
10893 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10894 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10895 GtkTextIter start_iter
, end_iter
;
10897 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10899 if (contents
== NULL
)
10902 glong len
= g_utf8_strlen(contents
, -1);
10903 if (len
> MAX_ALLOCA_MEM_SIZE
) {
10904 alertpanel_error(_("Size of pasted text exceeds limit (%dKiB) for paste.\n"
10905 "Attach as file instead."),
10906 (MAX_ALLOCA_MEM_SIZE
/ 1024));
10909 /* we shouldn't delete the selection when middle-click-pasting, or we
10910 * can't mid-click-paste our own selection */
10911 if (clip
!= GDK_SELECTION_PRIMARY
) {
10912 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10913 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10916 if (insert_place
== NULL
) {
10917 /* if insert_place isn't specified, insert at the cursor.
10918 * used for Ctrl-V pasting */
10919 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10920 start
= gtk_text_iter_get_offset(&start_iter
);
10921 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10923 /* if insert_place is specified, paste here.
10924 * used for mid-click-pasting */
10925 start
= gtk_text_iter_get_offset(insert_place
);
10926 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10927 if (prefs_common
.primary_paste_unselects
)
10928 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10932 /* paste unwrapped: mark the paste so it's not wrapped later */
10933 end
= start
+ strlen(contents
);
10934 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10935 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10936 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10937 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10938 /* rewrap paragraph now (after a mid-click-paste) */
10939 mark_start
= gtk_text_buffer_get_insert(buffer
);
10940 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10941 gtk_text_iter_backward_char(&start_iter
);
10942 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10944 } else if (GTK_IS_EDITABLE(entry
))
10945 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10947 compose
->modified
= TRUE
;
10950 static void entry_allsel(GtkWidget
*entry
)
10952 if (GTK_IS_EDITABLE(entry
))
10953 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10954 else if (GTK_IS_TEXT_VIEW(entry
)) {
10955 GtkTextIter startiter
, enditer
;
10956 GtkTextBuffer
*textbuf
;
10958 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10959 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10960 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10962 gtk_text_buffer_move_mark_by_name(textbuf
,
10963 "selection_bound", &startiter
);
10964 gtk_text_buffer_move_mark_by_name(textbuf
,
10965 "insert", &enditer
);
10969 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10971 Compose
*compose
= (Compose
*)data
;
10972 if (compose
->focused_editable
10973 #ifndef GENERIC_UMPC
10974 && gtk_widget_has_focus(compose
->focused_editable
)
10977 entry_cut_clipboard(compose
->focused_editable
);
10980 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10982 Compose
*compose
= (Compose
*)data
;
10983 if (compose
->focused_editable
10984 #ifndef GENERIC_UMPC
10985 && gtk_widget_has_focus(compose
->focused_editable
)
10988 entry_copy_clipboard(compose
->focused_editable
);
10991 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10993 Compose
*compose
= (Compose
*)data
;
10994 gint prev_autowrap
;
10995 GtkTextBuffer
*buffer
;
10997 if (compose
->focused_editable
10998 #ifndef GENERIC_UMPC
10999 && gtk_widget_has_focus(compose
->focused_editable
)
11002 entry_paste_clipboard(compose
, compose
->focused_editable
,
11003 prefs_common
.linewrap_pastes
,
11004 GDK_SELECTION_CLIPBOARD
, NULL
);
11009 #ifndef GENERIC_UMPC
11010 gtk_widget_has_focus(compose
->text
) &&
11012 compose
->gtkaspell
&&
11013 compose
->gtkaspell
->check_while_typing
)
11014 gtkaspell_highlight_all(compose
->gtkaspell
);
11018 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11020 Compose
*compose
= (Compose
*)data
;
11021 gint wrap_quote
= prefs_common
.linewrap_quote
;
11022 if (compose
->focused_editable
11023 #ifndef GENERIC_UMPC
11024 && gtk_widget_has_focus(compose
->focused_editable
)
11027 /* let text_insert() (called directly or at a later time
11028 * after the gtk_editable_paste_clipboard) know that
11029 * text is to be inserted as a quotation. implemented
11030 * by using a simple refcount... */
11031 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11032 G_OBJECT(compose
->focused_editable
),
11033 "paste_as_quotation"));
11034 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11035 "paste_as_quotation",
11036 GINT_TO_POINTER(paste_as_quotation
+ 1));
11037 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11038 entry_paste_clipboard(compose
, compose
->focused_editable
,
11039 prefs_common
.linewrap_pastes
,
11040 GDK_SELECTION_CLIPBOARD
, NULL
);
11041 prefs_common
.linewrap_quote
= wrap_quote
;
11045 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11047 Compose
*compose
= (Compose
*)data
;
11048 gint prev_autowrap
;
11049 GtkTextBuffer
*buffer
;
11051 if (compose
->focused_editable
11052 #ifndef GENERIC_UMPC
11053 && gtk_widget_has_focus(compose
->focused_editable
)
11056 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11057 GDK_SELECTION_CLIPBOARD
, NULL
);
11062 #ifndef GENERIC_UMPC
11063 gtk_widget_has_focus(compose
->text
) &&
11065 compose
->gtkaspell
&&
11066 compose
->gtkaspell
->check_while_typing
)
11067 gtkaspell_highlight_all(compose
->gtkaspell
);
11071 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11073 Compose
*compose
= (Compose
*)data
;
11074 gint prev_autowrap
;
11075 GtkTextBuffer
*buffer
;
11077 if (compose
->focused_editable
11078 #ifndef GENERIC_UMPC
11079 && gtk_widget_has_focus(compose
->focused_editable
)
11082 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11083 GDK_SELECTION_CLIPBOARD
, NULL
);
11088 #ifndef GENERIC_UMPC
11089 gtk_widget_has_focus(compose
->text
) &&
11091 compose
->gtkaspell
&&
11092 compose
->gtkaspell
->check_while_typing
)
11093 gtkaspell_highlight_all(compose
->gtkaspell
);
11097 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11099 Compose
*compose
= (Compose
*)data
;
11100 if (compose
->focused_editable
11101 #ifndef GENERIC_UMPC
11102 && gtk_widget_has_focus(compose
->focused_editable
)
11105 entry_allsel(compose
->focused_editable
);
11108 static void textview_move_beginning_of_line (GtkTextView
*text
)
11110 GtkTextBuffer
*buffer
;
11114 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11116 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11117 mark
= gtk_text_buffer_get_insert(buffer
);
11118 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11119 gtk_text_iter_set_line_offset(&ins
, 0);
11120 gtk_text_buffer_place_cursor(buffer
, &ins
);
11123 static void textview_move_forward_character (GtkTextView
*text
)
11125 GtkTextBuffer
*buffer
;
11129 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11131 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11132 mark
= gtk_text_buffer_get_insert(buffer
);
11133 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11134 if (gtk_text_iter_forward_cursor_position(&ins
))
11135 gtk_text_buffer_place_cursor(buffer
, &ins
);
11138 static void textview_move_backward_character (GtkTextView
*text
)
11140 GtkTextBuffer
*buffer
;
11144 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11146 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11147 mark
= gtk_text_buffer_get_insert(buffer
);
11148 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11149 if (gtk_text_iter_backward_cursor_position(&ins
))
11150 gtk_text_buffer_place_cursor(buffer
, &ins
);
11153 static void textview_move_forward_word (GtkTextView
*text
)
11155 GtkTextBuffer
*buffer
;
11160 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11162 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11163 mark
= gtk_text_buffer_get_insert(buffer
);
11164 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11165 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11166 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11167 gtk_text_iter_backward_word_start(&ins
);
11168 gtk_text_buffer_place_cursor(buffer
, &ins
);
11172 static void textview_move_backward_word (GtkTextView
*text
)
11174 GtkTextBuffer
*buffer
;
11178 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11180 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11181 mark
= gtk_text_buffer_get_insert(buffer
);
11182 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11183 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11184 gtk_text_buffer_place_cursor(buffer
, &ins
);
11187 static void textview_move_end_of_line (GtkTextView
*text
)
11189 GtkTextBuffer
*buffer
;
11193 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11195 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11196 mark
= gtk_text_buffer_get_insert(buffer
);
11197 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11198 if (gtk_text_iter_forward_to_line_end(&ins
))
11199 gtk_text_buffer_place_cursor(buffer
, &ins
);
11202 static void textview_move_next_line (GtkTextView
*text
)
11204 GtkTextBuffer
*buffer
;
11209 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11211 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11212 mark
= gtk_text_buffer_get_insert(buffer
);
11213 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11214 offset
= gtk_text_iter_get_line_offset(&ins
);
11215 if (gtk_text_iter_forward_line(&ins
)) {
11216 gtk_text_iter_set_line_offset(&ins
, offset
);
11217 gtk_text_buffer_place_cursor(buffer
, &ins
);
11221 static void textview_move_previous_line (GtkTextView
*text
)
11223 GtkTextBuffer
*buffer
;
11228 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11230 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11231 mark
= gtk_text_buffer_get_insert(buffer
);
11232 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11233 offset
= gtk_text_iter_get_line_offset(&ins
);
11234 if (gtk_text_iter_backward_line(&ins
)) {
11235 gtk_text_iter_set_line_offset(&ins
, offset
);
11236 gtk_text_buffer_place_cursor(buffer
, &ins
);
11240 static void textview_delete_forward_character (GtkTextView
*text
)
11242 GtkTextBuffer
*buffer
;
11244 GtkTextIter ins
, end_iter
;
11246 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11248 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11249 mark
= gtk_text_buffer_get_insert(buffer
);
11250 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11252 if (gtk_text_iter_forward_char(&end_iter
)) {
11253 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11257 static void textview_delete_backward_character (GtkTextView
*text
)
11259 GtkTextBuffer
*buffer
;
11261 GtkTextIter ins
, end_iter
;
11263 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11265 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11266 mark
= gtk_text_buffer_get_insert(buffer
);
11267 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11269 if (gtk_text_iter_backward_char(&end_iter
)) {
11270 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11274 static void textview_delete_forward_word (GtkTextView
*text
)
11276 GtkTextBuffer
*buffer
;
11278 GtkTextIter ins
, end_iter
;
11280 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11282 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11283 mark
= gtk_text_buffer_get_insert(buffer
);
11284 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11286 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11287 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11291 static void textview_delete_backward_word (GtkTextView
*text
)
11293 GtkTextBuffer
*buffer
;
11295 GtkTextIter ins
, end_iter
;
11297 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11299 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11300 mark
= gtk_text_buffer_get_insert(buffer
);
11301 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11303 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11304 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11308 static void textview_delete_line (GtkTextView
*text
)
11310 GtkTextBuffer
*buffer
;
11312 GtkTextIter ins
, start_iter
, end_iter
;
11314 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11316 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11317 mark
= gtk_text_buffer_get_insert(buffer
);
11318 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11321 gtk_text_iter_set_line_offset(&start_iter
, 0);
11324 if (gtk_text_iter_ends_line(&end_iter
)){
11325 if (!gtk_text_iter_forward_char(&end_iter
))
11326 gtk_text_iter_backward_char(&start_iter
);
11329 gtk_text_iter_forward_to_line_end(&end_iter
);
11330 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11333 static void textview_delete_to_line_end (GtkTextView
*text
)
11335 GtkTextBuffer
*buffer
;
11337 GtkTextIter ins
, end_iter
;
11339 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11341 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11342 mark
= gtk_text_buffer_get_insert(buffer
);
11343 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11345 if (gtk_text_iter_ends_line(&end_iter
))
11346 gtk_text_iter_forward_char(&end_iter
);
11348 gtk_text_iter_forward_to_line_end(&end_iter
);
11349 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11352 #define DO_ACTION(name, act) { \
11353 if(!strcmp(name, a_name)) { \
11357 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11359 const gchar
*a_name
= gtk_action_get_name(action
);
11360 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11361 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11362 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11363 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11364 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11365 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11366 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11367 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11368 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11369 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11370 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11371 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11372 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11373 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11374 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11377 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11379 Compose
*compose
= (Compose
*)data
;
11380 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11381 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11383 action
= compose_call_advanced_action_from_path(gaction
);
11386 void (*do_action
) (GtkTextView
*text
);
11387 } action_table
[] = {
11388 {textview_move_beginning_of_line
},
11389 {textview_move_forward_character
},
11390 {textview_move_backward_character
},
11391 {textview_move_forward_word
},
11392 {textview_move_backward_word
},
11393 {textview_move_end_of_line
},
11394 {textview_move_next_line
},
11395 {textview_move_previous_line
},
11396 {textview_delete_forward_character
},
11397 {textview_delete_backward_character
},
11398 {textview_delete_forward_word
},
11399 {textview_delete_backward_word
},
11400 {textview_delete_line
},
11401 {textview_delete_to_line_end
}
11404 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11406 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11407 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11408 if (action_table
[action
].do_action
)
11409 action_table
[action
].do_action(text
);
11411 g_warning("not implemented yet");
11415 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11417 GtkAllocation allocation
;
11421 if (GTK_IS_EDITABLE(widget
)) {
11422 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11423 gtk_editable_set_position(GTK_EDITABLE(widget
),
11426 if ((parent
= gtk_widget_get_parent(widget
))
11427 && (parent
= gtk_widget_get_parent(parent
))
11428 && (parent
= gtk_widget_get_parent(parent
))) {
11429 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11430 gtk_widget_get_allocation(widget
, &allocation
);
11431 gint y
= allocation
.y
;
11432 gint height
= allocation
.height
;
11433 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11434 (GTK_SCROLLED_WINDOW(parent
));
11436 gfloat value
= gtk_adjustment_get_value(shown
);
11437 gfloat upper
= gtk_adjustment_get_upper(shown
);
11438 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11439 if (y
< (int)value
) {
11440 gtk_adjustment_set_value(shown
, y
- 1);
11442 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11443 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11444 gtk_adjustment_set_value(shown
,
11445 y
+ height
- (int)page_size
- 1);
11447 gtk_adjustment_set_value(shown
,
11448 (int)upper
- (int)page_size
- 1);
11455 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11456 compose
->focused_editable
= widget
;
11458 #ifdef GENERIC_UMPC
11459 if (GTK_IS_TEXT_VIEW(widget
)
11460 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11461 g_object_ref(compose
->notebook
);
11462 g_object_ref(compose
->edit_vbox
);
11463 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11464 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11465 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11466 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11467 g_object_unref(compose
->notebook
);
11468 g_object_unref(compose
->edit_vbox
);
11469 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11470 G_CALLBACK(compose_grab_focus_cb
),
11472 gtk_widget_grab_focus(widget
);
11473 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11474 G_CALLBACK(compose_grab_focus_cb
),
11476 } else if (!GTK_IS_TEXT_VIEW(widget
)
11477 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11478 g_object_ref(compose
->notebook
);
11479 g_object_ref(compose
->edit_vbox
);
11480 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11481 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11482 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11483 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11484 g_object_unref(compose
->notebook
);
11485 g_object_unref(compose
->edit_vbox
);
11486 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11487 G_CALLBACK(compose_grab_focus_cb
),
11489 gtk_widget_grab_focus(widget
);
11490 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11491 G_CALLBACK(compose_grab_focus_cb
),
11497 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11499 compose
->modified
= TRUE
;
11500 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11501 #ifndef GENERIC_UMPC
11502 compose_set_title(compose
);
11506 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11508 Compose
*compose
= (Compose
*)data
;
11509 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11512 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11514 Compose
*compose
= (Compose
*)data
;
11515 compose_wrap_all_full(compose
, TRUE
);
11518 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11520 Compose
*compose
= (Compose
*)data
;
11522 message_search_compose(compose
);
11525 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11528 Compose
*compose
= (Compose
*)data
;
11529 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11530 if (compose
->autowrap
)
11531 compose_wrap_all_full(compose
, TRUE
);
11532 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11535 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11538 Compose
*compose
= (Compose
*)data
;
11539 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11542 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11544 Compose
*compose
= (Compose
*)data
;
11546 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11547 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11550 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11552 Compose
*compose
= (Compose
*)data
;
11554 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11555 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11558 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11560 g_free(compose
->privacy_system
);
11561 g_free(compose
->encdata
);
11563 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11564 compose_update_privacy_system_menu_item(compose
, warn
);
11567 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11569 if (folder_item
!= NULL
) {
11570 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11571 privacy_system_can_sign(compose
->privacy_system
)) {
11572 compose_use_signing(compose
,
11573 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11575 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11576 privacy_system_can_encrypt(compose
->privacy_system
)) {
11577 compose_use_encryption(compose
,
11578 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11583 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11585 Compose
*compose
= (Compose
*)data
;
11587 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11588 gtk_widget_show(compose
->ruler_hbox
);
11589 prefs_common
.show_ruler
= TRUE
;
11591 gtk_widget_hide(compose
->ruler_hbox
);
11592 gtk_widget_queue_resize(compose
->edit_vbox
);
11593 prefs_common
.show_ruler
= FALSE
;
11597 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11598 GdkDragContext
*context
,
11601 GtkSelectionData
*data
,
11604 gpointer user_data
)
11606 Compose
*compose
= (Compose
*)user_data
;
11610 type
= gtk_selection_data_get_data_type(data
);
11611 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11612 && gtk_drag_get_source_widget(context
) !=
11613 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11614 list
= uri_list_extract_filenames(
11615 (const gchar
*)gtk_selection_data_get_data(data
));
11616 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11617 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11618 compose_attach_append
11619 (compose
, (const gchar
*)tmp
->data
,
11620 utf8_filename
, NULL
, NULL
);
11621 g_free(utf8_filename
);
11624 compose_changed_cb(NULL
, compose
);
11625 list_free_strings_full(list
);
11626 } else if (gtk_drag_get_source_widget(context
)
11627 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11628 /* comes from our summaryview */
11629 SummaryView
* summaryview
= NULL
;
11630 GSList
* list
= NULL
, *cur
= NULL
;
11632 if (mainwindow_get_mainwindow())
11633 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11636 list
= summary_get_selected_msg_list(summaryview
);
11638 for (cur
= list
; cur
; cur
= cur
->next
) {
11639 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11640 gchar
*file
= NULL
;
11642 file
= procmsg_get_message_file_full(msginfo
,
11645 compose_attach_append(compose
, (const gchar
*)file
,
11646 (const gchar
*)file
, "message/rfc822", NULL
);
11650 g_slist_free(list
);
11654 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11655 GdkDragContext
*drag_context
,
11657 guint time
, gpointer user_data
)
11659 /* not handling this signal makes compose_insert_drag_received_cb
11664 static gboolean completion_set_focus_to_subject
11665 (GtkWidget
*widget
,
11666 GdkEventKey
*event
,
11669 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11671 /* make backtab move to subject field */
11672 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11673 gtk_widget_grab_focus(compose
->subject_entry
);
11679 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11680 GdkDragContext
*drag_context
,
11683 GtkSelectionData
*data
,
11686 gpointer user_data
)
11688 Compose
*compose
= (Compose
*)user_data
;
11694 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11696 type
= gtk_selection_data_get_data_type(data
);
11697 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11698 AlertValue val
= G_ALERTDEFAULT
;
11699 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11701 list
= uri_list_extract_filenames(ddata
);
11702 num_files
= g_list_length(list
);
11703 if (list
== NULL
&& strstr(ddata
, "://")) {
11704 /* Assume a list of no files, and data has ://, is a remote link */
11705 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11706 gchar
*tmpfile
= get_tmp_file();
11707 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11709 compose_insert_file(compose
, tmpfile
);
11710 claws_unlink(tmpfile
);
11712 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11713 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11716 switch (prefs_common
.compose_dnd_mode
) {
11717 case COMPOSE_DND_ASK
:
11718 msg
= g_strdup_printf(
11720 "Do you want to insert the contents of the file "
11721 "into the message body, or attach it to the email?",
11722 "Do you want to insert the contents of the %d files "
11723 "into the message body, or attach them to the email?",
11726 val
= alertpanel_full(_("Insert or attach?"), msg
,
11727 NULL
, _("_Cancel"), NULL
, _("_Insert"), NULL
, _("_Attach"),
11728 ALERTFOCUS_SECOND
, TRUE
, NULL
, ALERT_QUESTION
);
11731 case COMPOSE_DND_INSERT
:
11732 val
= G_ALERTALTERNATE
;
11734 case COMPOSE_DND_ATTACH
:
11735 val
= G_ALERTOTHER
;
11738 /* unexpected case */
11739 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11742 if (val
& G_ALERTDISABLE
) {
11743 val
&= ~G_ALERTDISABLE
;
11744 /* remember what action to perform by default, only if we don't click Cancel */
11745 if (val
== G_ALERTALTERNATE
)
11746 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11747 else if (val
== G_ALERTOTHER
)
11748 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11751 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11752 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11753 list_free_strings_full(list
);
11755 } else if (val
== G_ALERTOTHER
) {
11756 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11757 list_free_strings_full(list
);
11761 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11762 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11764 list_free_strings_full(list
);
11765 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11770 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11771 GdkDragContext
*drag_context
,
11774 GtkSelectionData
*data
,
11777 gpointer user_data
)
11779 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11780 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11782 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11785 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11786 gchar
*decoded
=g_new(gchar
, strlen(email
));
11789 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11790 gtk_editable_delete_text(entry
, 0, -1);
11791 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11792 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11796 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11799 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11801 Compose
*compose
= (Compose
*)data
;
11803 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11804 compose
->return_receipt
= TRUE
;
11806 compose
->return_receipt
= FALSE
;
11809 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11811 Compose
*compose
= (Compose
*)data
;
11813 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11814 compose
->remove_references
= TRUE
;
11816 compose
->remove_references
= FALSE
;
11819 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11820 ComposeHeaderEntry
*headerentry
)
11822 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11823 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11824 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11828 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11829 GdkEventKey
*event
,
11830 ComposeHeaderEntry
*headerentry
)
11832 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11833 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11834 !(event
->state
& GDK_MODIFIER_MASK
) &&
11835 (event
->keyval
== GDK_KEY_BackSpace
) &&
11836 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11837 gtk_container_remove
11838 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11839 headerentry
->combo
);
11840 gtk_container_remove
11841 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11842 headerentry
->entry
);
11843 headerentry
->compose
->header_list
=
11844 g_slist_remove(headerentry
->compose
->header_list
,
11846 g_free(headerentry
);
11847 } else if (event
->keyval
== GDK_KEY_Tab
) {
11848 if (headerentry
->compose
->header_last
== headerentry
) {
11849 /* Override default next focus, and give it to subject_entry
11850 * instead of notebook tabs
11852 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11853 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11860 static gboolean
scroll_postpone(gpointer data
)
11862 Compose
*compose
= (Compose
*)data
;
11864 if (compose
->batch
)
11867 GTK_EVENTS_FLUSH();
11868 compose_show_first_last_header(compose
, FALSE
);
11872 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11873 ComposeHeaderEntry
*headerentry
)
11875 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11876 compose_create_header_entry(headerentry
->compose
);
11877 g_signal_handlers_disconnect_matched
11878 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11879 0, 0, NULL
, NULL
, headerentry
);
11881 if (!headerentry
->compose
->batch
)
11882 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11886 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11888 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11889 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11893 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11895 GtkAdjustment
*vadj
;
11897 cm_return_if_fail(compose
);
11902 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11903 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11904 vadj
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(gtk_widget_get_parent(compose
->header_table
)));
11905 gtk_adjustment_set_value(vadj
, (show_first
?
11906 gtk_adjustment_get_lower(vadj
) :
11907 (gtk_adjustment_get_upper(vadj
) -
11908 gtk_adjustment_get_page_size(vadj
))));
11911 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11912 const gchar
*text
, gint len
, Compose
*compose
)
11914 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11915 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11918 cm_return_if_fail(text
!= NULL
);
11920 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11921 G_CALLBACK(text_inserted
),
11923 if (paste_as_quotation
) {
11925 const gchar
*qmark
;
11927 GtkTextIter start_iter
;
11930 len
= strlen(text
);
11932 new_text
= g_strndup(text
, len
);
11934 qmark
= compose_quote_char_from_context(compose
);
11936 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11937 gtk_text_buffer_place_cursor(buffer
, iter
);
11939 pos
= gtk_text_iter_get_offset(iter
);
11941 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11942 _("Quote format error at line %d."));
11943 quote_fmt_reset_vartable();
11945 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11946 GINT_TO_POINTER(paste_as_quotation
- 1));
11948 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11949 gtk_text_buffer_place_cursor(buffer
, iter
);
11950 gtk_text_buffer_delete_mark(buffer
, mark
);
11952 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11953 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11954 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11955 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11956 gtk_text_buffer_delete_mark(buffer
, mark
);
11958 if (strcmp(text
, "\n") || compose
->automatic_break
11959 || gtk_text_iter_starts_line(iter
)) {
11960 GtkTextIter before_ins
;
11961 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11962 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11963 before_ins
= *iter
;
11964 gtk_text_iter_backward_chars(&before_ins
, len
);
11965 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11968 /* check if the preceding is just whitespace or quote */
11969 GtkTextIter start_line
;
11970 gchar
*tmp
= NULL
, *quote
= NULL
;
11971 gint quote_len
= 0, is_normal
= 0;
11972 start_line
= *iter
;
11973 gtk_text_iter_set_line_offset(&start_line
, 0);
11974 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11977 if (*tmp
== '\0') {
11980 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11988 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11990 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11991 iter
, text
, len
, "no_join", NULL
);
11996 if (!paste_as_quotation
) {
11997 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11998 compose_beautify_paragraph(compose
, iter
, FALSE
);
11999 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12000 gtk_text_buffer_delete_mark(buffer
, mark
);
12003 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12004 G_CALLBACK(text_inserted
),
12006 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12008 if (compose_can_autosave(compose
) &&
12009 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12010 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12011 compose
->draft_timeout_tag
= g_timeout_add
12012 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12016 static void compose_check_all(GtkAction
*action
, gpointer data
)
12018 Compose
*compose
= (Compose
*)data
;
12019 if (!compose
->gtkaspell
)
12022 if (gtk_widget_has_focus(compose
->subject_entry
))
12023 claws_spell_entry_check_all(
12024 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12026 gtkaspell_check_all(compose
->gtkaspell
);
12029 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12031 Compose
*compose
= (Compose
*)data
;
12032 if (compose
->gtkaspell
) {
12033 claws_spell_entry_recheck_all(
12034 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12035 gtkaspell_highlight_all(compose
->gtkaspell
);
12039 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12041 Compose
*compose
= (Compose
*)data
;
12042 if (!compose
->gtkaspell
) {
12043 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12047 if (gtk_widget_has_focus(compose
->subject_entry
))
12048 claws_spell_entry_check_backwards(
12049 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12051 gtkaspell_check_backwards(compose
->gtkaspell
);
12054 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12056 Compose
*compose
= (Compose
*)data
;
12057 if (!compose
->gtkaspell
) {
12058 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12062 if (gtk_widget_has_focus(compose
->subject_entry
))
12063 claws_spell_entry_check_forwards_go(
12064 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12066 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12071 *\brief Guess originating forward account from MsgInfo and several
12072 * "common preference" settings. Return NULL if no guess.
12074 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12076 PrefsAccount
*account
= NULL
;
12078 cm_return_val_if_fail(msginfo
, NULL
);
12079 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12080 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12082 if (msginfo
->folder
->prefs
->enable_default_account
)
12083 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12085 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12087 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12088 extract_address(to
);
12089 account
= account_find_from_address(to
, FALSE
);
12092 if (!account
&& prefs_common
.forward_account_autosel
) {
12094 if (!procheader_get_header_from_msginfo
12095 (msginfo
, &cc
, "Cc:")) {
12096 gchar
*buf
= cc
+ strlen("Cc:");
12097 extract_address(buf
);
12098 account
= account_find_from_address(buf
, FALSE
);
12103 if (!account
&& prefs_common
.forward_account_autosel
) {
12104 gchar
*deliveredto
= NULL
;
12105 if (!procheader_get_header_from_msginfo
12106 (msginfo
, &deliveredto
, "Delivered-To:")) {
12107 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12108 extract_address(buf
);
12109 account
= account_find_from_address(buf
, FALSE
);
12110 g_free(deliveredto
);
12115 account
= msginfo
->folder
->folder
->account
;
12120 gboolean
compose_close(Compose
*compose
)
12124 cm_return_val_if_fail(compose
, FALSE
);
12126 if (!g_mutex_trylock(&compose
->mutex
)) {
12127 /* we have to wait for the (possibly deferred by auto-save)
12128 * drafting to be done, before destroying the compose under
12130 debug_print("waiting for drafting to finish...\n");
12131 compose_allow_user_actions(compose
, FALSE
);
12132 if (compose
->close_timeout_tag
== 0) {
12133 compose
->close_timeout_tag
=
12134 g_timeout_add (500, (GSourceFunc
) compose_close
,
12140 if (compose
->draft_timeout_tag
>= 0) {
12141 g_source_remove(compose
->draft_timeout_tag
);
12142 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12145 gtk_window_get_position(GTK_WINDOW(compose
->window
), &x
, &y
);
12146 if (!compose
->batch
) {
12147 prefs_common
.compose_x
= x
;
12148 prefs_common
.compose_y
= y
;
12150 g_mutex_unlock(&compose
->mutex
);
12151 compose_destroy(compose
);
12156 * Add entry field for each address in list.
12157 * \param compose E-Mail composition object.
12158 * \param listAddress List of (formatted) E-Mail addresses.
12160 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12163 node
= listAddress
;
12165 addr
= ( gchar
* ) node
->data
;
12166 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12167 node
= g_list_next( node
);
12171 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12172 guint action
, gboolean opening_multiple
)
12174 gchar
*body
= NULL
;
12175 GSList
*new_msglist
= NULL
;
12176 MsgInfo
*tmp_msginfo
= NULL
;
12177 gboolean originally_enc
= FALSE
;
12178 gboolean originally_sig
= FALSE
;
12179 Compose
*compose
= NULL
;
12180 gchar
*s_system
= NULL
;
12182 cm_return_if_fail(msginfo_list
!= NULL
);
12184 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12185 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12186 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12188 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12189 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12190 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12191 orig_msginfo
, mimeinfo
);
12192 if (tmp_msginfo
!= NULL
) {
12193 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12195 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12196 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12197 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12199 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12200 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12201 if (orig_msginfo
->tags
) {
12202 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12203 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12209 if (!opening_multiple
&& msgview
!= NULL
)
12210 body
= messageview_get_selection(msgview
);
12213 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12214 procmsg_msginfo_free(&tmp_msginfo
);
12215 g_slist_free(new_msglist
);
12217 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12219 if (compose
&& originally_enc
) {
12220 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12223 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12224 compose_force_signing(compose
, compose
->account
, s_system
);
12228 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12231 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12234 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12235 && msginfo_list
!= NULL
12236 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12237 GSList
*cur
= msginfo_list
;
12238 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12239 "messages. Opening the windows "
12240 "could take some time. Do you "
12241 "want to continue?"),
12242 g_slist_length(msginfo_list
));
12243 if (g_slist_length(msginfo_list
) > 9
12244 && alertpanel(_("Warning"), msg
, NULL
, _("_Cancel"), NULL
, _("_Yes"),
12245 NULL
, NULL
, ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12250 /* We'll open multiple compose windows */
12251 /* let the WM place the next windows */
12252 compose_force_window_origin
= FALSE
;
12253 for (; cur
; cur
= cur
->next
) {
12255 tmplist
.data
= cur
->data
;
12256 tmplist
.next
= NULL
;
12257 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12259 compose_force_window_origin
= TRUE
;
12261 /* forwarding multiple mails as attachments is done via a
12262 * single compose window */
12263 if (msginfo_list
!= NULL
) {
12264 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12265 } else if (msgview
!= NULL
) {
12267 tmplist
.data
= msgview
->msginfo
;
12268 tmplist
.next
= NULL
;
12269 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12271 debug_print("Nothing to reply to\n");
12276 void compose_check_for_email_account(Compose
*compose
)
12278 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12284 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12285 ac
= account_get_cur_account();
12286 if (ac
->protocol
== A_NNTP
) {
12287 list
= account_get_list();
12289 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12290 curr
= (PrefsAccount
*) list
->data
;
12291 if (curr
->protocol
!= A_NNTP
) {
12297 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12302 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12303 const gchar
*address
)
12305 GSList
*msginfo_list
= NULL
;
12306 gchar
*body
= messageview_get_selection(msgview
);
12309 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12311 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12312 compose_check_for_email_account(compose
);
12313 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12314 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12315 compose_reply_set_subject(compose
, msginfo
);
12318 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12321 void compose_set_position(Compose
*compose
, gint pos
)
12323 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12325 gtkut_text_view_set_position(text
, pos
);
12328 gboolean
compose_search_string(Compose
*compose
,
12329 const gchar
*str
, gboolean case_sens
)
12331 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12333 return gtkut_text_view_search_string(text
, str
, case_sens
);
12336 gboolean
compose_search_string_backward(Compose
*compose
,
12337 const gchar
*str
, gboolean case_sens
)
12339 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12341 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12344 /* allocate a msginfo structure and populate its data from a compose data structure */
12345 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12347 MsgInfo
*newmsginfo
;
12349 gchar date
[RFC822_DATE_BUFFSIZE
];
12351 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12353 newmsginfo
= procmsg_msginfo_new();
12356 get_rfc822_date(date
, sizeof(date
));
12357 newmsginfo
->date
= g_strdup(date
);
12360 if (compose
->from_name
) {
12361 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12362 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12366 if (compose
->subject_entry
)
12367 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12369 /* to, cc, reply-to, newsgroups */
12370 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12371 gchar
*header
= gtk_editable_get_chars(
12373 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12374 gchar
*entry
= gtk_editable_get_chars(
12375 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12377 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12378 if ( newmsginfo
->to
== NULL
) {
12379 newmsginfo
->to
= g_strdup(entry
);
12380 } else if (entry
&& *entry
) {
12381 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12382 g_free(newmsginfo
->to
);
12383 newmsginfo
->to
= tmp
;
12386 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12387 if ( newmsginfo
->cc
== NULL
) {
12388 newmsginfo
->cc
= g_strdup(entry
);
12389 } else if (entry
&& *entry
) {
12390 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12391 g_free(newmsginfo
->cc
);
12392 newmsginfo
->cc
= tmp
;
12395 if ( strcasecmp(header
,
12396 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12397 if ( newmsginfo
->newsgroups
== NULL
) {
12398 newmsginfo
->newsgroups
= g_strdup(entry
);
12399 } else if (entry
&& *entry
) {
12400 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12401 g_free(newmsginfo
->newsgroups
);
12402 newmsginfo
->newsgroups
= tmp
;
12410 /* other data is unset */
12416 /* update compose's dictionaries from folder dict settings */
12417 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12418 FolderItem
*folder_item
)
12420 cm_return_if_fail(compose
!= NULL
);
12422 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12423 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12425 if (prefs
->enable_default_dictionary
)
12426 gtkaspell_change_dict(compose
->gtkaspell
,
12427 prefs
->default_dictionary
, FALSE
);
12428 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12429 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12430 prefs
->default_alt_dictionary
);
12431 if (prefs
->enable_default_dictionary
12432 || prefs
->enable_default_alt_dictionary
)
12433 compose_spell_menu_changed(compose
);
12438 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12440 Compose
*compose
= (Compose
*)data
;
12442 cm_return_if_fail(compose
!= NULL
);
12444 gtk_widget_grab_focus(compose
->text
);
12447 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12449 gtk_combo_box_popup(GTK_COMBO_BOX(data
));