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\nUse \"Send queued messages\" from "
5422 "the main window to retry."), 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 if (compose
->account
->protocol
!= A_NNTP
)
6161 mailac
= compose
->account
;
6162 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
6163 mailac
= cur_account
;
6164 else if (!(mailac
= compose_current_mail_account())) {
6165 alertpanel_error(_("No account for sending mails available!"));
6166 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6170 if (compose
->newsgroup_list
) {
6171 if (compose
->account
->protocol
== A_NNTP
)
6172 newsac
= compose
->account
;
6174 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6175 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6179 /* write queue header */
6180 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6181 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6182 debug_print("queuing to %s\n", tmp
);
6183 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6184 FILE_OP_ERROR(tmp
, "claws_fopen");
6186 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6189 if (change_file_mode_rw(fp
, tmp
) < 0) {
6190 FILE_OP_ERROR(tmp
, "chmod");
6191 g_warning("can't change file mode");
6194 /* queueing variables */
6195 err
|= (fprintf(fp
, "AF:\n") < 0);
6196 err
|= (fprintf(fp
, "NF:0\n") < 0);
6197 err
|= (fprintf(fp
, "PS:10\n") < 0);
6198 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6199 err
|= (fprintf(fp
, "SFN:\n") < 0);
6200 err
|= (fprintf(fp
, "DSR:\n") < 0);
6202 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6204 err
|= (fprintf(fp
, "MID:\n") < 0);
6205 err
|= (fprintf(fp
, "CFG:\n") < 0);
6206 err
|= (fprintf(fp
, "PT:0\n") < 0);
6207 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6208 err
|= (fprintf(fp
, "RQ:\n") < 0);
6210 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6212 err
|= (fprintf(fp
, "SSV:\n") < 0);
6214 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6216 err
|= (fprintf(fp
, "NSV:\n") < 0);
6217 err
|= (fprintf(fp
, "SSH:\n") < 0);
6218 /* write recipient list */
6219 if (compose
->to_list
) {
6220 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6221 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6223 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6224 err
|= (fprintf(fp
, "\n") < 0);
6226 /* write newsgroup list */
6227 if (compose
->newsgroup_list
) {
6228 err
|= (fprintf(fp
, "NG:") < 0);
6229 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6230 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6231 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6232 err
|= (fprintf(fp
, "\n") < 0);
6236 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6238 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6241 if (compose
->privacy_system
!= NULL
) {
6242 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6243 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6244 if (compose
->use_encryption
) {
6245 if (!compose_warn_encryption(compose
)) {
6249 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6251 if (mailac
&& mailac
->encrypt_to_self
) {
6252 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6253 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6254 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6255 g_slist_free(tmp_list
);
6257 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6259 if (compose
->encdata
!= NULL
) {
6260 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6261 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6262 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6263 compose
->encdata
) < 0);
6264 } /* else we finally dont want to encrypt */
6266 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6267 /* and if encdata was null, it means there's been a problem in
6270 g_warning("failed to write queue message");
6274 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6279 /* Save copy folder */
6280 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6281 gchar
*savefolderid
;
6283 savefolderid
= compose_get_save_to(compose
);
6284 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6285 g_free(savefolderid
);
6287 /* Save copy folder */
6288 if (compose
->return_receipt
) {
6289 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6291 /* Message-ID of message replying to */
6292 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6293 gchar
*folderid
= NULL
;
6295 if (compose
->replyinfo
->folder
)
6296 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6297 if (folderid
== NULL
)
6298 folderid
= g_strdup("NULL");
6300 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6303 /* Message-ID of message forwarding to */
6304 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6305 gchar
*folderid
= NULL
;
6307 if (compose
->fwdinfo
->folder
)
6308 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6309 if (folderid
== NULL
)
6310 folderid
= g_strdup("NULL");
6312 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6316 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6317 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6319 /* end of headers */
6320 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6322 if (compose
->redirect_filename
!= NULL
) {
6323 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6327 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6331 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6339 g_warning("failed to write queue message");
6343 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6345 if (claws_safe_fclose(fp
) == EOF
) {
6346 FILE_OP_ERROR(tmp
, "claws_fclose");
6349 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6352 if (item
&& *item
) {
6355 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6358 g_warning("can't find queue folder");
6361 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6363 folder_item_scan(queue
);
6364 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6365 g_warning("can't queue the message");
6368 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6371 if (msgpath
== NULL
) {
6377 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6378 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6380 procmsg_msginfo_change_flags(mi
,
6381 compose
->targetinfo
->flags
.perm_flags
,
6382 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6385 g_slist_free(mi
->tags
);
6386 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6387 procmsg_msginfo_free(&mi
);
6391 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6392 compose_remove_reedit_target(compose
, FALSE
);
6395 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6400 return COMPOSE_QUEUE_SUCCESS
;
6403 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6406 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6411 GError
*error
= NULL
;
6416 gchar
*type
, *subtype
;
6417 GtkTreeModel
*model
;
6420 model
= gtk_tree_view_get_model(tree_view
);
6422 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6425 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6427 if (!is_file_exist(ainfo
->file
)) {
6428 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6429 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6430 NULL
, _("Cancel sending"),
6431 NULL
, _("Ignore attachment"), NULL
, NULL
,
6432 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6434 if (val
== G_ALERTDEFAULT
) {
6440 f
= g_file_new_for_path(ainfo
->file
);
6441 fi
= g_file_query_info(f
, "standard::size",
6442 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6443 if (error
!= NULL
) {
6444 g_warning(error
->message
);
6445 g_error_free(error
);
6449 size
= g_file_info_get_size(fi
);
6453 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6455 size
= statbuf
.st_size
;
6458 mimepart
= procmime_mimeinfo_new();
6459 mimepart
->content
= MIMECONTENT_FILE
;
6460 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6461 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6462 mimepart
->offset
= 0;
6463 mimepart
->length
= size
;
6465 type
= g_strdup(ainfo
->content_type
);
6467 if (!strchr(type
, '/')) {
6469 type
= g_strdup("application/octet-stream");
6472 subtype
= strchr(type
, '/') + 1;
6473 *(subtype
- 1) = '\0';
6474 mimepart
->type
= procmime_get_media_type(type
);
6475 mimepart
->subtype
= g_strdup(subtype
);
6478 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6479 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6480 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6481 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6482 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6483 /* Text parts with no name come from multipart/alternative
6484 * forwards. Make sure the recipient won't look at the
6485 * original HTML part by mistake. */
6486 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6487 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6491 g_hash_table_insert(mimepart
->typeparameters
,
6492 g_strdup("charset"), g_strdup(ainfo
->charset
));
6494 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6495 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6496 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6497 g_hash_table_insert(mimepart
->typeparameters
,
6498 g_strdup("name"), g_strdup(ainfo
->name
));
6499 g_hash_table_insert(mimepart
->dispositionparameters
,
6500 g_strdup("filename"), g_strdup(ainfo
->name
));
6501 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6504 if (mimepart
->type
== MIMETYPE_MESSAGE
6505 || mimepart
->type
== MIMETYPE_MULTIPART
)
6506 ainfo
->encoding
= ENC_BINARY
;
6507 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6508 if (ainfo
->encoding
== ENC_7BIT
)
6509 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6510 else if (ainfo
->encoding
== ENC_8BIT
)
6511 ainfo
->encoding
= ENC_BASE64
;
6514 procmime_encode_content(mimepart
, ainfo
->encoding
);
6516 g_node_append(parent
->node
, mimepart
->node
);
6517 } while (gtk_tree_model_iter_next(model
, &iter
));
6522 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6524 GSList
*list
= NULL
, *item
= NULL
;
6525 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6527 list
= address_list_append_with_comments(list
, str
);
6528 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6529 gchar
*spec
= item
->data
;
6530 gchar
*endofname
= strstr(spec
, " <");
6531 if (endofname
!= NULL
) {
6534 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6535 qqname
= escape_internal_quotes(qname
, '"');
6537 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6538 gchar
*addr
= g_strdup(endofname
);
6539 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6540 faddr
= g_strconcat(name
, addr
, NULL
);
6543 debug_print("new auto-quoted address: '%s'\n", faddr
);
6547 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6549 gchar
*tmp
= g_strconcat(result
,
6551 (faddr
!= NULL
)? faddr
: spec
,
6556 if (faddr
!= NULL
) {
6561 slist_free_strings_full(list
);
6566 #define IS_IN_CUSTOM_HEADER(header) \
6567 (compose->account->add_customhdr && \
6568 custom_header_find(compose->account->customhdr_list, header) != NULL)
6570 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6572 /* return the untranslated header name, if header_name is a known
6573 header name, in either its translated or untranslated form, with
6574 or without trailing colon. otherwise, returns header_name. */
6575 gchar
*translated_header_name
;
6576 gchar
*translated_header_name_wcolon
;
6577 const gchar
*untranslated_header_name
;
6578 const gchar
*untranslated_header_name_wcolon
;
6581 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6583 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6584 untranslated_header_name
= HEADERS
[i
].header_name
;
6585 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6587 translated_header_name
= gettext(untranslated_header_name
);
6588 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6590 if (!strcmp(header_name
, untranslated_header_name
) ||
6591 !strcmp(header_name
, translated_header_name
)) {
6592 return untranslated_header_name
;
6594 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6595 !strcmp(header_name
, translated_header_name_wcolon
)) {
6596 return untranslated_header_name_wcolon
;
6600 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6604 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6606 const gchar
*fieldname
,
6607 const gchar
*seperator
)
6609 gchar
*str
, *fieldname_w_colon
;
6610 gboolean add_field
= FALSE
;
6612 ComposeHeaderEntry
*headerentry
;
6613 const gchar
*headerentryname
;
6614 const gchar
*trans_fieldname
;
6617 if (IS_IN_CUSTOM_HEADER(fieldname
))
6620 debug_print("Adding %s-fields\n", fieldname
);
6622 fieldstr
= g_string_sized_new(64);
6624 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6625 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6627 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6628 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6629 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6631 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6632 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6634 str
= compose_quote_list_of_addresses(ustr
);
6636 if (str
!= NULL
&& str
[0] != '\0') {
6638 g_string_append(fieldstr
, seperator
);
6639 g_string_append(fieldstr
, str
);
6648 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6649 compose_convert_header
6650 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6651 strlen(fieldname
) + 2, TRUE
);
6652 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6656 g_free(fieldname_w_colon
);
6657 g_string_free(fieldstr
, TRUE
);
6662 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6664 GString
*sh_header
= g_string_new(" ");
6666 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6668 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6669 ComposeHeaderEntry
*headerentry
;
6672 gchar
*headername_wcolon
;
6673 const gchar
*headername_trans
;
6675 gboolean standard_header
= FALSE
;
6677 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6679 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6681 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6686 if (!strstr(tmp
, ":")) {
6687 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6688 headername
= g_strdup(tmp
);
6690 headername_wcolon
= g_strdup(tmp
);
6691 headername
= g_strdup(strtok(tmp
, ":"));
6695 string
= std_headers
;
6696 while (*string
!= NULL
) {
6697 headername_trans
= prefs_common_translated_header_name(*string
);
6698 if (!strcmp(headername_trans
, headername_wcolon
))
6699 standard_header
= TRUE
;
6702 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6703 g_string_append_printf(sh_header
, "%s ", headername
);
6705 g_free(headername_wcolon
);
6707 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6708 return g_string_free(sh_header
, FALSE
);
6711 static gchar
*compose_get_header(Compose
*compose
)
6713 gchar date
[RFC822_DATE_BUFFSIZE
];
6714 gchar buf
[BUFFSIZE
];
6715 const gchar
*entry_str
;
6719 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6721 gchar
*from_name
= NULL
, *from_address
= NULL
;
6724 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6725 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6727 header
= g_string_sized_new(64);
6730 if (prefs_common
.hide_timezone
)
6731 get_rfc822_date_hide_tz(date
, sizeof(date
));
6733 get_rfc822_date(date
, sizeof(date
));
6734 g_string_append_printf(header
, "Date: %s\n", date
);
6738 if (compose
->account
->name
&& *compose
->account
->name
) {
6740 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6741 tmp
= g_strdup_printf("%s <%s>",
6742 buf
, compose
->account
->address
);
6744 tmp
= g_strdup_printf("%s",
6745 compose
->account
->address
);
6747 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6748 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6750 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6751 from_address
= g_strdup(compose
->account
->address
);
6753 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6754 /* extract name and address */
6755 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6756 from_address
= g_strdup(strrchr(spec
, '<')+1);
6757 *(strrchr(from_address
, '>')) = '\0';
6758 from_name
= g_strdup(spec
);
6759 *(strrchr(from_name
, '<')) = '\0';
6762 from_address
= g_strdup(spec
);
6769 if (from_name
&& *from_name
) {
6771 compose_convert_header
6772 (compose
, buf
, sizeof(buf
), from_name
,
6773 strlen("From: "), TRUE
);
6774 QUOTE_IF_REQUIRED(name
, buf
);
6775 qname
= escape_internal_quotes(name
, '"');
6777 g_string_append_printf(header
, "From: %s <%s>\n",
6778 qname
, from_address
);
6779 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6780 compose
->return_receipt
) {
6781 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6782 strlen("Disposition-Notification-To: "),
6784 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6789 g_string_append_printf(header
, "From: %s\n", from_address
);
6790 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6791 compose
->return_receipt
)
6792 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6796 g_free(from_address
);
6799 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6802 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6805 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6809 * If this account is a NNTP account remove Bcc header from
6810 * message body since it otherwise will be publicly shown
6812 if (compose
->account
->protocol
!= A_NNTP
)
6813 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6816 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6818 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6821 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6822 strlen("Subject: "), FALSE
);
6823 g_string_append_printf(header
, "Subject: %s\n", buf
);
6829 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6830 g_string_append_printf(header
, "Message-ID: <%s>\n",
6834 if (compose
->remove_references
== FALSE
) {
6836 if (compose
->inreplyto
&& compose
->to_list
)
6837 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6840 if (compose
->references
)
6841 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6845 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6848 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6851 if (compose
->account
->organization
&&
6852 strlen(compose
->account
->organization
) &&
6853 !IS_IN_CUSTOM_HEADER("Organization")) {
6854 compose_convert_header(compose
, buf
, sizeof(buf
),
6855 compose
->account
->organization
,
6856 strlen("Organization: "), FALSE
);
6857 g_string_append_printf(header
, "Organization: %s\n", buf
);
6860 /* Program version and system info */
6861 if (compose
->account
->gen_xmailer
&&
6862 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6863 !compose
->newsgroup_list
) {
6864 g_string_append_printf(header
, "X-Mailer: %s (GTK %d.%d.%d; %s)\n",
6866 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6869 if (compose
->account
->gen_xmailer
&&
6870 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6871 g_string_append_printf(header
, "X-Newsreader: %s (GTK %d.%d.%d; %s)\n",
6873 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6877 /* custom headers */
6878 if (compose
->account
->add_customhdr
) {
6881 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6883 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6885 if (custom_header_is_allowed(chdr
->name
)
6886 && chdr
->value
!= NULL
6887 && *(chdr
->value
) != '\0') {
6888 compose_convert_header
6889 (compose
, buf
, sizeof(buf
),
6891 strlen(chdr
->name
) + 2, FALSE
);
6892 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6897 /* Automatic Faces and X-Faces */
6898 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6899 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6901 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6902 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6904 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6905 g_string_append_printf(header
, "Face: %s\n", buf
);
6907 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6908 g_string_append_printf(header
, "Face: %s\n", buf
);
6912 switch (compose
->priority
) {
6913 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6914 "X-Priority: 1 (Highest)\n");
6916 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6917 "X-Priority: 2 (High)\n");
6919 case PRIORITY_NORMAL
: break;
6920 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6921 "X-Priority: 4 (Low)\n");
6923 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6924 "X-Priority: 5 (Lowest)\n");
6926 default: debug_print("compose: priority unknown : %d\n",
6930 /* get special headers */
6931 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6932 ComposeHeaderEntry
*headerentry
;
6935 gchar
*headername_wcolon
;
6936 const gchar
*headername_trans
;
6939 gboolean standard_header
= FALSE
;
6941 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6943 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6945 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6950 if (!strstr(tmp
, ":")) {
6951 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6952 headername
= g_strdup(tmp
);
6954 headername_wcolon
= g_strdup(tmp
);
6955 headername
= g_strdup(strtok(tmp
, ":"));
6959 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6960 Xstrdup_a(headervalue
, entry_str
, {
6962 g_free(headername_wcolon
);
6963 g_string_free(header
, TRUE
);
6966 subst_char(headervalue
, '\r', ' ');
6967 subst_char(headervalue
, '\n', ' ');
6968 g_strstrip(headervalue
);
6969 if (*headervalue
!= '\0') {
6970 string
= std_headers
;
6971 while (*string
!= NULL
&& !standard_header
) {
6972 headername_trans
= prefs_common_translated_header_name(*string
);
6973 /* support mixed translated and untranslated headers */
6974 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6975 standard_header
= TRUE
;
6978 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6979 /* store untranslated header name */
6980 g_string_append_printf(header
, "%s %s\n",
6981 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6985 g_free(headername_wcolon
);
6989 g_string_free(header
, FALSE
);
6994 #undef IS_IN_CUSTOM_HEADER
6996 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6997 gint header_len
, gboolean addr_field
)
6999 gchar
*tmpstr
= NULL
;
7000 const gchar
*out_codeset
= NULL
;
7002 cm_return_if_fail(src
!= NULL
);
7003 cm_return_if_fail(dest
!= NULL
);
7005 if (len
< 1) return;
7007 tmpstr
= g_strdup(src
);
7009 subst_char(tmpstr
, '\n', ' ');
7010 subst_char(tmpstr
, '\r', ' ');
7013 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
7014 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
7015 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
7020 codeconv_set_strict(TRUE
);
7021 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7022 conv_get_charset_str(compose
->out_encoding
));
7023 codeconv_set_strict(FALSE
);
7025 if (!dest
|| *dest
== '\0') {
7026 gchar
*test_conv_global_out
= NULL
;
7027 gchar
*test_conv_reply
= NULL
;
7029 /* automatic mode. be automatic. */
7030 codeconv_set_strict(TRUE
);
7032 out_codeset
= conv_get_outgoing_charset_str();
7034 debug_print("trying to convert to %s\n", out_codeset
);
7035 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7038 if (!test_conv_global_out
&& compose
->orig_charset
7039 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7040 out_codeset
= compose
->orig_charset
;
7041 debug_print("failure; trying to convert to %s\n", out_codeset
);
7042 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7045 if (!test_conv_global_out
&& !test_conv_reply
) {
7047 out_codeset
= CS_INTERNAL
;
7048 debug_print("finally using %s\n", out_codeset
);
7050 g_free(test_conv_global_out
);
7051 g_free(test_conv_reply
);
7052 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7054 codeconv_set_strict(FALSE
);
7059 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7063 cm_return_if_fail(user_data
!= NULL
);
7065 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7066 g_strstrip(address
);
7067 if (*address
!= '\0') {
7068 gchar
*name
= procheader_get_fromname(address
);
7069 extract_address(address
);
7070 #ifndef USE_ALT_ADDRBOOK
7071 addressbook_add_contact(name
, address
, NULL
, NULL
);
7073 debug_print("%s: %s\n", name
, address
);
7074 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7075 debug_print( "addressbook_add_contact - added\n" );
7082 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7084 GtkWidget
*menuitem
;
7087 cm_return_if_fail(menu
!= NULL
);
7088 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7090 menuitem
= gtk_separator_menu_item_new();
7091 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7092 gtk_widget_show(menuitem
);
7094 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7095 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7097 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7098 g_strstrip(address
);
7099 if (*address
== '\0') {
7100 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7103 g_signal_connect(G_OBJECT(menuitem
), "activate",
7104 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7105 gtk_widget_show(menuitem
);
7108 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7111 if (strcmp(header
, "")) {
7112 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7116 void compose_add_extra_header_entries(GtkListStore
*model
)
7120 gchar buf
[BUFFSIZE
];
7123 if (extra_headers
== NULL
) {
7124 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7125 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7126 debug_print("extra headers file not found\n");
7127 goto extra_headers_done
;
7129 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7130 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7131 while (lastc
>= 0 && buf
[lastc
] != ':')
7132 buf
[lastc
--] = '\0';
7133 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7134 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7135 if (custom_header_is_allowed(buf
)) {
7137 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7140 g_message("disallowed extra header line: %s\n", buf
);
7144 g_message("invalid extra header line: %s\n", buf
);
7150 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7151 extra_headers
= g_slist_reverse(extra_headers
);
7153 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7157 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7159 LdapServer
*server
= (LdapServer
*)data
;
7160 gboolean
*enable
= (gboolean
*)user_data
;
7162 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7163 server
->searchFlag
= *enable
;
7167 static void compose_create_header_entry(Compose
*compose
)
7169 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7176 const gchar
*header
= NULL
;
7177 ComposeHeaderEntry
*headerentry
;
7178 gboolean standard_header
= FALSE
;
7179 GtkListStore
*model
;
7182 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7184 /* Combo box model */
7185 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7186 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7188 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7190 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7192 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7193 COMPOSE_NEWSGROUPS
);
7194 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7196 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7197 COMPOSE_FOLLOWUPTO
);
7198 compose_add_extra_header_entries(model
);
7201 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7202 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7203 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7204 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7205 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7206 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7207 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7208 G_CALLBACK(compose_grab_focus_cb
), compose
);
7209 gtk_widget_show(combo
);
7211 gtk_grid_attach(GTK_GRID(compose
->header_table
), combo
, 0, compose
->header_nextrow
,
7213 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7214 const gchar
*last_header_entry
= gtk_entry_get_text(
7215 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7217 while (*string
!= NULL
) {
7218 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7219 standard_header
= TRUE
;
7222 if (standard_header
)
7223 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7225 if (!compose
->header_last
|| !standard_header
) {
7226 switch(compose
->account
->protocol
) {
7228 header
= prefs_common_translated_header_name("Newsgroups:");
7231 header
= prefs_common_translated_header_name("To:");
7236 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7238 gtk_editable_set_editable(
7239 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7240 prefs_common
.type_any_header
);
7242 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7243 G_CALLBACK(compose_grab_focus_cb
), compose
);
7245 /* Entry field with cleanup button */
7246 button
= gtk_button_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU
);
7247 gtk_widget_show(button
);
7248 CLAWS_SET_TIP(button
,
7249 _("Delete entry contents"));
7250 entry
= gtk_entry_new();
7251 gtk_widget_show(entry
);
7252 CLAWS_SET_TIP(entry
,
7253 _("Use <tab> to autocomplete from addressbook"));
7254 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
7255 gtk_widget_show(hbox
);
7256 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7257 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7258 gtk_grid_attach(GTK_GRID(compose
->header_table
), hbox
, 1, compose
->header_nextrow
,
7260 gtk_widget_set_hexpand(hbox
, TRUE
);
7261 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
7263 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7264 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7266 g_signal_connect(G_OBJECT(entry
), "changed",
7267 G_CALLBACK(compose_headerentry_changed_cb
),
7269 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7270 G_CALLBACK(compose_grab_focus_cb
), compose
);
7272 g_signal_connect(G_OBJECT(button
), "clicked",
7273 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7277 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7278 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7279 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7280 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7281 G_CALLBACK(compose_header_drag_received_cb
),
7283 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7284 G_CALLBACK(compose_drag_drop
),
7286 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7287 G_CALLBACK(compose_entry_popup_extend
),
7291 #ifndef PASSWORD_CRYPTO_OLD
7292 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7293 if (pwd_servers
!= NULL
&& primary_passphrase() == NULL
) {
7294 gboolean enable
= FALSE
;
7295 debug_print("Primary passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7296 /* Temporarily disable password-protected LDAP servers,
7297 * because user did not provide a primary passphrase.
7298 * We can safely enable searchFlag on all servers in this list
7299 * later, since addrindex_get_password_protected_ldap_servers()
7300 * includes servers which have it enabled initially. */
7301 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7302 compose
->passworded_ldap_servers
= pwd_servers
;
7304 #endif /* PASSWORD_CRYPTO_OLD */
7305 #endif /* USE_LDAP */
7307 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7309 headerentry
->compose
= compose
;
7310 headerentry
->combo
= combo
;
7311 headerentry
->entry
= entry
;
7312 headerentry
->button
= button
;
7313 headerentry
->hbox
= hbox
;
7314 headerentry
->headernum
= compose
->header_nextrow
;
7315 headerentry
->type
= PREF_NONE
;
7317 compose
->header_nextrow
++;
7318 compose
->header_last
= headerentry
;
7319 compose
->header_list
=
7320 g_slist_append(compose
->header_list
,
7324 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7325 gchar
*text
, ComposePrefType pref_type
)
7327 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7328 gchar
*tmp
= g_strdup(text
), *email
;
7329 gboolean replyto_hdr
;
7331 replyto_hdr
= (!strcasecmp(header
,
7332 prefs_common_translated_header_name("Reply-To:")) ||
7334 prefs_common_translated_header_name("Followup-To:")) ||
7336 prefs_common_translated_header_name("In-Reply-To:")));
7338 extract_address(tmp
);
7339 email
= g_utf8_strdown(tmp
, -1);
7341 if (replyto_hdr
== FALSE
&&
7342 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7344 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7345 header
, text
, (gint
) pref_type
);
7351 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7352 gtk_entry_set_text(GTK_ENTRY(
7353 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7355 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7356 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7357 last_header
->type
= pref_type
;
7359 if (replyto_hdr
== FALSE
)
7360 g_hash_table_insert(compose
->email_hashtable
, email
,
7361 GUINT_TO_POINTER(1));
7368 static void compose_destroy_headerentry(Compose
*compose
,
7369 ComposeHeaderEntry
*headerentry
)
7371 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7374 extract_address(text
);
7375 email
= g_utf8_strdown(text
, -1);
7376 g_hash_table_remove(compose
->email_hashtable
, email
);
7380 gtk_widget_destroy(headerentry
->combo
);
7381 gtk_widget_destroy(headerentry
->entry
);
7382 gtk_widget_destroy(headerentry
->button
);
7383 gtk_widget_destroy(headerentry
->hbox
);
7384 g_free(headerentry
);
7387 static void compose_remove_header_entries(Compose
*compose
)
7390 for (list
= compose
->header_list
; list
; list
= list
->next
)
7391 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7393 compose
->header_last
= NULL
;
7394 g_slist_free(compose
->header_list
);
7395 compose
->header_list
= NULL
;
7396 compose
->header_nextrow
= 1;
7397 compose_create_header_entry(compose
);
7400 static GtkWidget
*compose_create_header(Compose
*compose
)
7402 GtkWidget
*from_optmenu_hbox
;
7403 GtkWidget
*header_table_main
;
7404 GtkWidget
*header_scrolledwin
;
7405 GtkWidget
*header_table
;
7407 /* parent with account selection and from header */
7408 header_table_main
= gtk_grid_new();
7409 gtk_widget_show(header_table_main
);
7410 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7412 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7413 gtk_grid_attach(GTK_GRID(header_table_main
),from_optmenu_hbox
, 0, 0, 1, 1);
7414 gtk_widget_set_hexpand(from_optmenu_hbox
, TRUE
);
7415 gtk_widget_set_halign(from_optmenu_hbox
, GTK_ALIGN_FILL
);
7417 /* child with header labels and entries */
7418 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7419 gtk_widget_show(header_scrolledwin
);
7420 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7422 header_table
= gtk_grid_new();
7423 gtk_widget_show(header_table
);
7424 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7425 gtk_container_add(GTK_CONTAINER(header_scrolledwin
), header_table
);
7426 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7427 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7428 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7430 gtk_grid_attach(GTK_GRID(header_table_main
), header_scrolledwin
, 0, 1, 1, 1);
7431 gtk_widget_set_vexpand(header_scrolledwin
, TRUE
);
7432 gtk_widget_set_valign(header_scrolledwin
, GTK_ALIGN_FILL
);
7434 compose
->header_table
= header_table
;
7435 compose
->header_list
= NULL
;
7436 compose
->header_nextrow
= 0;
7438 compose_create_header_entry(compose
);
7440 compose
->table
= NULL
;
7442 return header_table_main
;
7445 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7447 Compose
*compose
= (Compose
*)data
;
7448 GdkEventButton event
;
7451 event
.time
= gtk_get_current_event_time();
7453 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7456 static GtkWidget
*compose_create_attach(Compose
*compose
)
7458 GtkWidget
*attach_scrwin
;
7459 GtkWidget
*attach_clist
;
7461 GtkListStore
*store
;
7462 GtkCellRenderer
*renderer
;
7463 GtkTreeViewColumn
*column
;
7464 GtkTreeSelection
*selection
;
7466 /* attachment list */
7467 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7468 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7469 GTK_POLICY_AUTOMATIC
,
7470 GTK_POLICY_AUTOMATIC
);
7471 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7473 store
= gtk_list_store_new(N_ATTACH_COLS
,
7479 G_TYPE_AUTO_POINTER
,
7481 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7482 (GTK_TREE_MODEL(store
)));
7483 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7484 g_object_unref(store
);
7486 renderer
= gtk_cell_renderer_text_new();
7487 column
= gtk_tree_view_column_new_with_attributes
7488 (_("Mime type"), renderer
, "text",
7489 COL_MIMETYPE
, NULL
);
7490 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7492 renderer
= gtk_cell_renderer_text_new();
7493 column
= gtk_tree_view_column_new_with_attributes
7494 (_("Size"), renderer
, "text",
7496 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7498 renderer
= gtk_cell_renderer_text_new();
7499 column
= gtk_tree_view_column_new_with_attributes
7500 (_("Name"), renderer
, "text",
7502 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7504 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7505 prefs_common
.use_stripes_everywhere
);
7506 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7507 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7509 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7510 G_CALLBACK(attach_selected
), compose
);
7511 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7512 G_CALLBACK(attach_button_pressed
), compose
);
7513 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7514 G_CALLBACK(popup_attach_button_pressed
), compose
);
7515 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7516 G_CALLBACK(attach_key_pressed
), compose
);
7519 gtk_drag_dest_set(attach_clist
,
7520 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7521 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7522 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7523 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7524 G_CALLBACK(compose_attach_drag_received_cb
),
7526 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7527 G_CALLBACK(compose_drag_drop
),
7530 compose
->attach_scrwin
= attach_scrwin
;
7531 compose
->attach_clist
= attach_clist
;
7533 return attach_scrwin
;
7536 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7538 static GtkWidget
*compose_create_others(Compose
*compose
)
7541 GtkWidget
*savemsg_checkbtn
;
7542 GtkWidget
*savemsg_combo
;
7543 GtkWidget
*savemsg_select
;
7546 gchar
*folderidentifier
;
7548 /* Table for settings */
7549 table
= gtk_grid_new();
7550 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7551 gtk_widget_show(table
);
7552 gtk_grid_set_row_spacing(GTK_GRID(table
), VSPACING_NARROW
);
7555 /* Save Message to folder */
7556 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7557 gtk_widget_show(savemsg_checkbtn
);
7558 gtk_grid_attach(GTK_GRID(table
), savemsg_checkbtn
, 0, rowcount
, 1, 1);
7559 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7560 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7563 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7564 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7565 compose
->savemsg_combo
= savemsg_combo
;
7566 gtk_widget_show(savemsg_combo
);
7568 if (prefs_common
.compose_save_to_history
)
7569 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7570 prefs_common
.compose_save_to_history
);
7571 gtk_grid_attach(GTK_GRID(table
), savemsg_combo
, 1, rowcount
, 1, 1);
7572 gtk_widget_set_hexpand(savemsg_combo
, TRUE
);
7573 gtk_widget_set_halign(savemsg_combo
, GTK_ALIGN_FILL
);
7574 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7575 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7576 G_CALLBACK(compose_grab_focus_cb
), compose
);
7577 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7578 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7579 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7581 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7582 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7583 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7584 (compose
->account
, F_OUTBOX
));
7585 compose_set_save_to(compose
, folderidentifier
);
7586 g_free(folderidentifier
);
7589 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7590 gtk_widget_show(savemsg_select
);
7591 gtk_grid_attach(GTK_GRID(table
), savemsg_select
, 2, rowcount
, 1, 1);
7592 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7593 G_CALLBACK(compose_savemsg_select_cb
),
7599 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7604 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7605 _("Select folder to save message to"));
7608 path
= folder_item_get_identifier(dest
);
7610 compose_set_save_to(compose
, path
);
7614 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7615 GdkAtom clip
, GtkTextIter
*insert_place
);
7618 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7622 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7624 if (event
->button
== 3) {
7626 GtkTextIter sel_start
, sel_end
;
7627 gboolean stuff_selected
;
7629 /* move the cursor to allow GtkAspell to check the word
7630 * under the mouse */
7631 if (event
->x
&& event
->y
) {
7632 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7633 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7635 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7638 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7639 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7642 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7644 &sel_start
, &sel_end
);
7646 gtk_text_buffer_place_cursor (buffer
, &iter
);
7647 /* reselect stuff */
7649 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7650 gtk_text_buffer_select_range(buffer
,
7651 &sel_start
, &sel_end
);
7653 return FALSE
; /* pass the event so that the right-click goes through */
7656 if (event
->button
== 2) {
7661 /* get the middle-click position to paste at the correct place */
7662 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7663 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7665 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7668 entry_paste_clipboard(compose
, text
,
7669 prefs_common
.linewrap_pastes
,
7670 GDK_SELECTION_PRIMARY
, &iter
);
7678 static void compose_spell_menu_changed(void *data
)
7680 Compose
*compose
= (Compose
*)data
;
7682 GtkWidget
*menuitem
;
7683 GtkWidget
*parent_item
;
7684 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7687 if (compose
->gtkaspell
== NULL
)
7690 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7691 "/Menu/Spelling/Options");
7693 /* setting the submenu removes /Spelling/Options from the factory
7694 * so we need to save it */
7696 if (parent_item
== NULL
) {
7697 parent_item
= compose
->aspell_options_menu
;
7698 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7700 compose
->aspell_options_menu
= parent_item
;
7702 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7704 spell_menu
= g_slist_reverse(spell_menu
);
7705 for (items
= spell_menu
;
7706 items
; items
= items
->next
) {
7707 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7708 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7709 gtk_widget_show(GTK_WIDGET(menuitem
));
7711 g_slist_free(spell_menu
);
7713 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7714 gtk_widget_show(parent_item
);
7717 static void compose_dict_changed(void *data
)
7719 Compose
*compose
= (Compose
*) data
;
7721 if(!compose
->gtkaspell
)
7723 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7726 gtkaspell_highlight_all(compose
->gtkaspell
);
7727 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7731 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7733 Compose
*compose
= (Compose
*)data
;
7734 GdkEventButton event
;
7737 event
.time
= gtk_get_current_event_time();
7741 return text_clicked(compose
->text
, &event
, compose
);
7744 static gboolean compose_force_window_origin
= TRUE
;
7745 static Compose
*compose_create(PrefsAccount
*account
,
7754 GtkWidget
*handlebox
;
7756 GtkWidget
*notebook
;
7758 GtkWidget
*attach_hbox
;
7759 GtkWidget
*attach_lab1
;
7760 GtkWidget
*attach_lab2
;
7765 GtkWidget
*subject_hbox
;
7766 GtkWidget
*subject_frame
;
7767 GtkWidget
*subject_entry
;
7771 GtkWidget
*edit_vbox
;
7772 GtkWidget
*ruler_hbox
;
7774 GtkWidget
*scrolledwin
;
7776 GtkTextBuffer
*buffer
;
7777 GtkClipboard
*clipboard
;
7779 UndoMain
*undostruct
;
7781 GtkWidget
*popupmenu
;
7782 GtkWidget
*tmpl_menu
;
7783 GtkActionGroup
*action_group
= NULL
;
7786 GtkAspell
* gtkaspell
= NULL
;
7789 static GdkGeometry geometry
;
7790 GdkRectangle workarea
= {0};
7792 cm_return_val_if_fail(account
!= NULL
, NULL
);
7794 default_header_bgcolor
= prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7795 default_header_color
= prefs_common
.color
[COL_DEFAULT_HEADER
],
7797 debug_print("Creating compose window...\n");
7798 compose
= g_new0(Compose
, 1);
7800 compose
->batch
= batch
;
7801 compose
->account
= account
;
7802 compose
->folder
= folder
;
7804 g_mutex_init(&compose
->mutex
);
7805 compose
->set_cursor_pos
= -1;
7807 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7809 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7810 gtk_window_set_default_size(GTK_WINDOW(window
), prefs_common
.compose_width
,
7811 prefs_common
.compose_height
);
7813 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
7816 if (!geometry
.max_width
) {
7817 geometry
.max_width
= workarea
.width
;
7818 geometry
.max_height
= workarea
.height
;
7821 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7822 &geometry
, GDK_HINT_MAX_SIZE
);
7823 if (!geometry
.min_width
) {
7824 geometry
.min_width
= 600;
7825 geometry
.min_height
= 440;
7827 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7828 &geometry
, GDK_HINT_MIN_SIZE
);
7830 #ifndef GENERIC_UMPC
7831 if (compose_force_window_origin
)
7832 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7833 prefs_common
.compose_y
);
7835 g_signal_connect(G_OBJECT(window
), "delete_event",
7836 G_CALLBACK(compose_delete_cb
), compose
);
7837 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7838 gtk_widget_realize(window
);
7840 gtkut_widget_set_composer_icon(window
);
7842 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
7843 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7845 compose
->ui_manager
= gtk_ui_manager_new();
7846 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7847 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7848 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7849 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7850 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7851 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7852 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7853 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7854 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7855 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7859 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7860 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7869 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7870 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7874 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7885 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7886 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7888 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7890 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7895 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7897 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7900 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7906 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7908 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7913 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7915 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7918 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7919 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7921 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7923 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7924 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7930 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7933 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7941 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7944 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7946 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7947 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7950 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7951 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7952 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7953 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7954 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7955 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7956 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7958 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7959 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7960 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7961 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7962 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7964 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7966 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7967 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7968 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7969 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7970 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7972 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7973 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
)
7974 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
)
7975 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7977 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7979 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7980 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
)
7981 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
)
7983 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7986 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
)
7987 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7989 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7990 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
)
7991 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7993 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7995 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7996 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
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
8002 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
8003 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
)
8004 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
)
8005 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
8006 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
8008 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
8009 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
8010 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
8011 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
8012 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
8013 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
8015 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
8016 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
8017 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
)
8019 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
8020 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
8021 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
8025 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
8026 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8027 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8028 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8029 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8030 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8033 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8035 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8036 gtk_widget_show_all(menubar
);
8038 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8039 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8041 handlebox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8042 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8044 gtk_widget_realize(handlebox
);
8045 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8048 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 2);
8049 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8050 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8053 notebook
= gtk_notebook_new();
8054 gtk_widget_show(notebook
);
8056 /* header labels and entries */
8057 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8058 compose_create_header(compose
),
8059 gtk_label_new_with_mnemonic(_("Hea_der")));
8060 /* attachment list */
8061 attach_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8062 gtk_widget_show(attach_hbox
);
8064 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8065 gtk_widget_show(attach_lab1
);
8066 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8068 attach_lab2
= gtk_label_new("");
8069 gtk_widget_show(attach_lab2
);
8070 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8072 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8073 compose_create_attach(compose
),
8076 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8077 compose_create_others(compose
),
8078 gtk_label_new_with_mnemonic(_("Othe_rs")));
8081 subject_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8082 gtk_widget_show(subject_hbox
);
8084 subject_frame
= gtk_frame_new(NULL
);
8085 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8086 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8087 gtk_widget_show(subject_frame
);
8089 subject
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, HSPACING_NARROW
);
8090 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8091 gtk_widget_show(subject
);
8093 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8094 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8095 gtk_widget_show(label
);
8098 subject_entry
= claws_spell_entry_new();
8100 subject_entry
= gtk_entry_new();
8102 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8103 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8104 G_CALLBACK(compose_grab_focus_cb
), compose
);
8105 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8106 gtk_widget_show(subject_entry
);
8107 compose
->subject_entry
= subject_entry
;
8108 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8110 edit_vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
8112 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8115 ruler_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8116 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8118 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8119 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8120 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8124 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8125 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8126 GTK_POLICY_AUTOMATIC
,
8127 GTK_POLICY_AUTOMATIC
);
8128 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8130 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8132 text
= gtk_text_view_new();
8133 if (prefs_common
.show_compose_margin
) {
8134 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8135 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8137 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8138 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8139 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8140 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8141 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8143 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8144 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8145 G_CALLBACK(compose_edit_size_alloc
),
8147 g_signal_connect(G_OBJECT(buffer
), "changed",
8148 G_CALLBACK(compose_changed_cb
), compose
);
8149 g_signal_connect(G_OBJECT(text
), "grab_focus",
8150 G_CALLBACK(compose_grab_focus_cb
), compose
);
8151 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8152 G_CALLBACK(text_inserted
), compose
);
8153 g_signal_connect(G_OBJECT(text
), "button_press_event",
8154 G_CALLBACK(text_clicked
), compose
);
8155 g_signal_connect(G_OBJECT(text
), "popup-menu",
8156 G_CALLBACK(compose_popup_menu
), compose
);
8157 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8158 G_CALLBACK(compose_changed_cb
), compose
);
8159 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8160 G_CALLBACK(compose_subject_entry_activated
), compose
);
8163 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8164 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8165 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8166 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8167 G_CALLBACK(compose_insert_drag_received_cb
),
8169 g_signal_connect(G_OBJECT(text
), "drag-drop",
8170 G_CALLBACK(compose_drag_drop
),
8172 g_signal_connect(G_OBJECT(text
), "key-press-event",
8173 G_CALLBACK(completion_set_focus_to_subject
),
8175 gtk_widget_show_all(vbox
);
8177 /* pane between attach clist and text */
8178 paned
= gtk_paned_new(GTK_ORIENTATION_VERTICAL
);
8179 gtk_box_pack_start(GTK_BOX(vbox2
), paned
, TRUE
, TRUE
, 0);
8180 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8181 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8182 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8183 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8184 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8186 gtk_widget_show_all(paned
);
8189 if (prefs_common
.textfont
) {
8190 PangoFontDescription
*font_desc
;
8192 font_desc
= pango_font_description_from_string
8193 (prefs_common
.textfont
);
8195 gtk_widget_override_font(text
, font_desc
);
8196 pango_font_description_free(font_desc
);
8200 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8201 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8202 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8203 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8204 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8205 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8206 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8207 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8209 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8211 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8212 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8213 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8215 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8217 undostruct
= undo_init(text
);
8218 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8221 address_completion_start(window
);
8223 compose
->window
= window
;
8224 compose
->vbox
= vbox
;
8225 compose
->menubar
= menubar
;
8226 compose
->handlebox
= handlebox
;
8228 compose
->vbox2
= vbox2
;
8230 compose
->paned
= paned
;
8232 compose
->attach_label
= attach_lab2
;
8234 compose
->notebook
= notebook
;
8235 compose
->edit_vbox
= edit_vbox
;
8236 compose
->ruler_hbox
= ruler_hbox
;
8237 compose
->ruler
= ruler
;
8238 compose
->scrolledwin
= scrolledwin
;
8239 compose
->text
= text
;
8241 compose
->focused_editable
= NULL
;
8243 compose
->popupmenu
= popupmenu
;
8245 compose
->tmpl_menu
= tmpl_menu
;
8247 compose
->mode
= mode
;
8248 compose
->rmode
= mode
;
8250 compose
->targetinfo
= NULL
;
8251 compose
->replyinfo
= NULL
;
8252 compose
->fwdinfo
= NULL
;
8254 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8255 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8257 compose
->replyto
= NULL
;
8259 compose
->bcc
= NULL
;
8260 compose
->followup_to
= NULL
;
8262 compose
->ml_post
= NULL
;
8264 compose
->inreplyto
= NULL
;
8265 compose
->references
= NULL
;
8266 compose
->msgid
= NULL
;
8267 compose
->boundary
= NULL
;
8269 compose
->autowrap
= prefs_common
.autowrap
;
8270 compose
->autoindent
= prefs_common
.auto_indent
;
8271 compose
->use_signing
= FALSE
;
8272 compose
->use_encryption
= FALSE
;
8273 compose
->privacy_system
= NULL
;
8274 compose
->encdata
= NULL
;
8276 compose
->modified
= FALSE
;
8278 compose
->return_receipt
= FALSE
;
8280 compose
->to_list
= NULL
;
8281 compose
->newsgroup_list
= NULL
;
8283 compose
->undostruct
= undostruct
;
8285 compose
->sig_str
= NULL
;
8287 compose
->exteditor_file
= NULL
;
8288 compose
->exteditor_pid
= INVALID_PID
;
8289 compose
->exteditor_tag
= -1;
8290 compose
->exteditor_socket
= NULL
;
8291 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8293 compose
->folder_update_callback_id
=
8294 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8295 compose_update_folder_hook
,
8296 (gpointer
) compose
);
8299 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8300 if (mode
!= COMPOSE_REDIRECT
) {
8301 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8302 strcmp(prefs_common
.dictionary
, "")) {
8303 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8304 prefs_common
.alt_dictionary
,
8305 conv_get_locale_charset_str(),
8306 prefs_common
.color
[COL_MISSPELLED
],
8307 prefs_common
.check_while_typing
,
8308 prefs_common
.recheck_when_changing_dict
,
8309 prefs_common
.use_alternate
,
8310 prefs_common
.use_both_dicts
,
8311 GTK_TEXT_VIEW(text
),
8312 GTK_WINDOW(compose
->window
),
8313 compose_dict_changed
,
8314 compose_spell_menu_changed
,
8317 alertpanel_error(_("Spell checker could not "
8319 gtkaspell_checkers_strerror());
8320 gtkaspell_checkers_reset_error();
8322 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8326 compose
->gtkaspell
= gtkaspell
;
8327 compose_spell_menu_changed(compose
);
8328 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8331 compose_select_account(compose
, account
, TRUE
);
8333 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8334 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8336 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8337 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8339 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8340 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8342 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8343 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8345 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8346 if (account
->protocol
!= A_NNTP
)
8347 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8348 prefs_common_translated_header_name("To:"));
8350 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8351 prefs_common_translated_header_name("Newsgroups:"));
8353 #ifndef USE_ALT_ADDRBOOK
8354 addressbook_set_target_compose(compose
);
8356 if (mode
!= COMPOSE_REDIRECT
)
8357 compose_set_template_menu(compose
);
8359 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8362 compose_list
= g_list_append(compose_list
, compose
);
8364 if (!prefs_common
.show_ruler
)
8365 gtk_widget_hide(ruler_hbox
);
8367 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8370 compose
->priority
= PRIORITY_NORMAL
;
8371 compose_update_priority_menu_item(compose
);
8373 compose_set_out_encoding(compose
);
8376 compose_update_actions_menu(compose
);
8378 /* Privacy Systems menu */
8379 compose_update_privacy_systems_menu(compose
);
8380 compose_activate_privacy_system(compose
, account
, TRUE
);
8382 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8384 gtk_widget_realize(window
);
8386 gtk_widget_show(window
);
8392 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8397 GtkWidget
*optmenubox
;
8398 GtkWidget
*fromlabel
;
8401 GtkWidget
*from_name
= NULL
;
8403 gint num
= 0, def_menu
= 0;
8405 accounts
= account_get_list();
8406 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8408 optmenubox
= gtk_event_box_new();
8409 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8410 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8412 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 4);
8413 from_name
= gtk_entry_new();
8415 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8416 G_CALLBACK(compose_grab_focus_cb
), compose
);
8417 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8418 G_CALLBACK(from_name_activate_cb
), optmenu
);
8420 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8421 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8422 gchar
*name
, *from
= NULL
;
8424 if (ac
== compose
->account
) def_menu
= num
;
8426 name
= g_markup_printf_escaped("<i>%s</i>",
8429 if (ac
== compose
->account
) {
8430 if (ac
->name
&& *ac
->name
) {
8432 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8433 from
= g_strdup_printf("%s <%s>",
8435 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8437 from
= g_strdup_printf("%s",
8439 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8441 if (cur_account
!= compose
->account
) {
8444 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor
, color
);
8445 gtk_widget_modify_base(
8446 GTK_WIDGET(from_name
),
8447 GTK_STATE_NORMAL
, &color
);
8448 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color
, color
);
8449 gtk_widget_modify_text(
8450 GTK_WIDGET(from_name
),
8451 GTK_STATE_NORMAL
, &color
);
8454 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8459 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8461 g_signal_connect(G_OBJECT(optmenu
), "changed",
8462 G_CALLBACK(account_activated
),
8464 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8465 G_CALLBACK(compose_entry_popup_extend
),
8468 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8469 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8471 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8472 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8473 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8475 CLAWS_SET_TIP(optmenubox
,
8476 _("Account to use for this email"));
8477 CLAWS_SET_TIP(from_name
,
8478 _("Sender address to be used"));
8480 compose
->account_combo
= optmenu
;
8481 compose
->from_name
= from_name
;
8486 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8488 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8489 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8490 Compose
*compose
= (Compose
*) data
;
8492 compose
->priority
= value
;
8496 static void compose_reply_change_mode(Compose
*compose
,
8499 gboolean was_modified
= compose
->modified
;
8501 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8503 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8505 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8507 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8509 if (action
== COMPOSE_REPLY_TO_ALL
)
8511 if (action
== COMPOSE_REPLY_TO_SENDER
)
8513 if (action
== COMPOSE_REPLY_TO_LIST
)
8516 compose_remove_header_entries(compose
);
8517 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8518 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8519 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8521 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8522 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8524 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8525 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8526 compose_show_first_last_header(compose
, TRUE
);
8527 compose
->modified
= was_modified
;
8528 compose_set_title(compose
);
8531 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8533 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8534 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8535 Compose
*compose
= (Compose
*) data
;
8538 compose_reply_change_mode(compose
, value
);
8541 static void compose_update_priority_menu_item(Compose
* compose
)
8543 GtkWidget
*menuitem
= NULL
;
8544 switch (compose
->priority
) {
8545 case PRIORITY_HIGHEST
:
8546 menuitem
= gtk_ui_manager_get_widget
8547 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8550 menuitem
= gtk_ui_manager_get_widget
8551 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8553 case PRIORITY_NORMAL
:
8554 menuitem
= gtk_ui_manager_get_widget
8555 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8558 menuitem
= gtk_ui_manager_get_widget
8559 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8561 case PRIORITY_LOWEST
:
8562 menuitem
= gtk_ui_manager_get_widget
8563 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8566 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8569 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8571 Compose
*compose
= (Compose
*) data
;
8573 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8575 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8577 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8580 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8581 g_free(compose
->privacy_system
);
8582 compose
->privacy_system
= NULL
;
8583 g_free(compose
->encdata
);
8584 compose
->encdata
= NULL
;
8585 if (systemid
!= NULL
) {
8586 compose
->privacy_system
= g_strdup(systemid
);
8588 can_sign
= privacy_system_can_sign(systemid
);
8589 can_encrypt
= privacy_system_can_encrypt(systemid
);
8592 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8595 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8596 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8597 gtk_widget_set_sensitive(
8598 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8600 gtk_toggle_tool_button_set_active(
8601 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8602 can_sign
? compose
->use_signing
: FALSE
);
8604 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8605 gtk_widget_set_sensitive(
8606 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8608 gtk_toggle_tool_button_set_active(
8609 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8610 can_encrypt
? compose
->use_encryption
: FALSE
);
8614 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8616 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8617 GtkWidget
*menuitem
= NULL
;
8618 GList
*children
, *amenu
;
8619 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8620 gboolean found
= FALSE
;
8622 if (compose
->privacy_system
!= NULL
) {
8624 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8625 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8626 cm_return_if_fail(menuitem
!= NULL
);
8628 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8631 while (amenu
!= NULL
) {
8632 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8633 if (systemid
!= NULL
) {
8634 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8635 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8636 menuitem
= GTK_WIDGET(amenu
->data
);
8638 can_sign
= privacy_system_can_sign(systemid
);
8639 can_encrypt
= privacy_system_can_encrypt(systemid
);
8643 } else if (strlen(compose
->privacy_system
) == 0 &&
8644 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8645 menuitem
= GTK_WIDGET(amenu
->data
);
8648 can_encrypt
= FALSE
;
8653 amenu
= amenu
->next
;
8655 g_list_free(children
);
8656 if (menuitem
!= NULL
)
8657 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8659 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8660 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8661 "will not be able to sign or encrypt this message."),
8662 compose
->privacy_system
);
8666 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8667 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8668 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8669 gtk_widget_set_sensitive(
8670 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8673 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8674 gtk_widget_set_sensitive(
8675 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8680 static void compose_set_out_encoding(Compose
*compose
)
8682 CharSet out_encoding
;
8683 const gchar
*branch
= NULL
;
8684 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8686 switch(out_encoding
) {
8687 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8688 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8689 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8690 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8691 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8692 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8693 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8694 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8695 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8696 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8697 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8698 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8699 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8700 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8701 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8702 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8703 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8704 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8705 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8706 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8707 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8708 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8709 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8710 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8711 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8712 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8713 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8714 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8715 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8716 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8717 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8718 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8719 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8720 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8722 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8725 static void compose_set_template_menu(Compose
*compose
)
8727 GSList
*tmpl_list
, *cur
;
8731 tmpl_list
= template_get_config();
8733 menu
= gtk_menu_new();
8735 gtk_menu_set_accel_group (GTK_MENU (menu
),
8736 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8737 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8738 Template
*tmpl
= (Template
*)cur
->data
;
8739 gchar
*accel_path
= NULL
;
8740 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8741 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8742 g_signal_connect(G_OBJECT(item
), "activate",
8743 G_CALLBACK(compose_template_activate_cb
),
8745 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8746 gtk_widget_show(item
);
8747 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8748 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8752 gtk_widget_show(menu
);
8753 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8756 void compose_update_actions_menu(Compose
*compose
)
8758 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8761 static void compose_update_privacy_systems_menu(Compose
*compose
)
8763 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8764 GSList
*systems
, *cur
;
8766 GtkWidget
*system_none
;
8768 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8769 GtkWidget
*privacy_menu
= gtk_menu_new();
8771 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8772 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8774 g_signal_connect(G_OBJECT(system_none
), "activate",
8775 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8777 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8778 gtk_widget_show(system_none
);
8780 systems
= privacy_get_system_ids();
8781 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8782 gchar
*systemid
= cur
->data
;
8784 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8785 widget
= gtk_radio_menu_item_new_with_label(group
,
8786 privacy_system_get_name(systemid
));
8787 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8788 g_strdup(systemid
), g_free
);
8789 g_signal_connect(G_OBJECT(widget
), "activate",
8790 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8792 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8793 gtk_widget_show(widget
);
8796 g_slist_free(systems
);
8797 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8798 gtk_widget_show_all(privacy_menu
);
8799 gtk_widget_show_all(privacy_menuitem
);
8802 void compose_reflect_prefs_all(void)
8807 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8808 compose
= (Compose
*)cur
->data
;
8809 compose_set_template_menu(compose
);
8813 void compose_reflect_prefs_pixmap_theme(void)
8818 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8819 compose
= (Compose
*)cur
->data
;
8820 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8824 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8826 const gchar
*qmark
= NULL
;
8828 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8830 switch (compose
->mode
) {
8831 /* use forward-specific quote char */
8832 case COMPOSE_FORWARD
:
8833 case COMPOSE_FORWARD_AS_ATTACH
:
8834 case COMPOSE_FORWARD_INLINE
:
8835 if (compose
->folder
&& compose
->folder
->prefs
&&
8836 compose
->folder
->prefs
->forward_with_format
)
8837 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8838 else if (compose
->account
->forward_with_format
)
8839 qmark
= compose
->account
->forward_quotemark
;
8841 qmark
= prefs_common
.fw_quotemark
;
8844 /* use reply-specific quote char in all other modes */
8846 if (compose
->folder
&& compose
->folder
->prefs
&&
8847 compose
->folder
->prefs
->reply_with_format
)
8848 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8849 else if (compose
->account
->reply_with_format
)
8850 qmark
= compose
->account
->reply_quotemark
;
8852 qmark
= prefs_common
.quotemark
;
8856 if (qmark
== NULL
|| *qmark
== '\0')
8862 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8866 GtkTextBuffer
*buffer
;
8870 gchar
*parsed_str
= NULL
;
8871 gint cursor_pos
= 0;
8872 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8875 /* process the body */
8877 text
= GTK_TEXT_VIEW(compose
->text
);
8878 buffer
= gtk_text_view_get_buffer(text
);
8881 qmark
= compose_quote_char_from_context(compose
);
8883 if (compose
->replyinfo
!= NULL
) {
8886 gtk_text_buffer_set_text(buffer
, "", -1);
8887 mark
= gtk_text_buffer_get_insert(buffer
);
8888 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8890 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8891 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8893 } else if (compose
->fwdinfo
!= NULL
) {
8896 gtk_text_buffer_set_text(buffer
, "", -1);
8897 mark
= gtk_text_buffer_get_insert(buffer
);
8898 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8900 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8901 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8904 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8906 GtkTextIter start
, end
;
8909 gtk_text_buffer_get_start_iter(buffer
, &start
);
8910 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8911 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8913 /* clear the buffer now */
8915 gtk_text_buffer_set_text(buffer
, "", -1);
8917 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8918 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8919 procmsg_msginfo_free( &dummyinfo
);
8925 gtk_text_buffer_set_text(buffer
, "", -1);
8926 mark
= gtk_text_buffer_get_insert(buffer
);
8927 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8930 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8931 compose_insert_sig(compose
, FALSE
);
8933 if (replace
&& parsed_str
) {
8934 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8935 gtk_text_buffer_place_cursor(buffer
, &iter
);
8939 cursor_pos
= quote_fmt_get_cursor_pos();
8940 compose
->set_cursor_pos
= cursor_pos
;
8941 if (cursor_pos
== -1)
8943 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8944 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8945 gtk_text_buffer_place_cursor(buffer
, &iter
);
8948 /* process the other fields */
8950 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8951 compose_template_apply_fields(compose
, tmpl
);
8952 quote_fmt_reset_vartable();
8953 quote_fmtlex_destroy();
8955 compose_changed_cb(NULL
, compose
);
8958 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8959 gtkaspell_highlight_all(compose
->gtkaspell
);
8963 static void compose_template_apply_fields_error(const gchar
*header
)
8968 tr
= g_strdup(C_("'%s' stands for a header name",
8969 "Template '%s' format error."));
8970 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8971 alertpanel_error("%s", text
);
8977 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8979 MsgInfo
* dummyinfo
= NULL
;
8980 MsgInfo
*msginfo
= NULL
;
8983 if (compose
->replyinfo
!= NULL
)
8984 msginfo
= compose
->replyinfo
;
8985 else if (compose
->fwdinfo
!= NULL
)
8986 msginfo
= compose
->fwdinfo
;
8988 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8989 msginfo
= dummyinfo
;
8992 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8994 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8995 compose
->gtkaspell
);
8997 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8999 quote_fmt_scan_string(tmpl
->from
);
9002 buf
= quote_fmt_get_buffer();
9004 compose_template_apply_fields_error("From");
9006 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
9009 quote_fmt_reset_vartable();
9010 quote_fmtlex_destroy();
9013 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
9015 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9016 compose
->gtkaspell
);
9018 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9020 quote_fmt_scan_string(tmpl
->to
);
9023 buf
= quote_fmt_get_buffer();
9025 compose_template_apply_fields_error("To");
9027 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9030 quote_fmt_reset_vartable();
9031 quote_fmtlex_destroy();
9034 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9036 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9037 compose
->gtkaspell
);
9039 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9041 quote_fmt_scan_string(tmpl
->cc
);
9044 buf
= quote_fmt_get_buffer();
9046 compose_template_apply_fields_error("Cc");
9048 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9051 quote_fmt_reset_vartable();
9052 quote_fmtlex_destroy();
9055 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9057 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9058 compose
->gtkaspell
);
9060 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9062 quote_fmt_scan_string(tmpl
->bcc
);
9065 buf
= quote_fmt_get_buffer();
9067 compose_template_apply_fields_error("Bcc");
9069 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9072 quote_fmt_reset_vartable();
9073 quote_fmtlex_destroy();
9076 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9078 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9079 compose
->gtkaspell
);
9081 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9083 quote_fmt_scan_string(tmpl
->replyto
);
9086 buf
= quote_fmt_get_buffer();
9088 compose_template_apply_fields_error("Reply-To");
9090 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9093 quote_fmt_reset_vartable();
9094 quote_fmtlex_destroy();
9097 /* process the subject */
9098 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9100 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9101 compose
->gtkaspell
);
9103 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9105 quote_fmt_scan_string(tmpl
->subject
);
9108 buf
= quote_fmt_get_buffer();
9110 compose_template_apply_fields_error("Subject");
9112 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9115 quote_fmt_reset_vartable();
9116 quote_fmtlex_destroy();
9119 procmsg_msginfo_free( &dummyinfo
);
9122 static void compose_destroy(Compose
*compose
)
9124 GtkAllocation allocation
;
9125 GtkTextBuffer
*buffer
;
9126 GtkClipboard
*clipboard
;
9128 compose_list
= g_list_remove(compose_list
, compose
);
9131 gboolean enable
= TRUE
;
9132 g_slist_foreach(compose
->passworded_ldap_servers
,
9133 _ldap_srv_func
, &enable
);
9134 g_slist_free(compose
->passworded_ldap_servers
);
9137 if (compose
->updating
) {
9138 debug_print("danger, not destroying anything now\n");
9139 compose
->deferred_destroy
= TRUE
;
9143 /* NOTE: address_completion_end() does nothing with the window
9144 * however this may change. */
9145 address_completion_end(compose
->window
);
9147 slist_free_strings_full(compose
->to_list
);
9148 slist_free_strings_full(compose
->newsgroup_list
);
9149 slist_free_strings_full(compose
->header_list
);
9151 slist_free_strings_full(extra_headers
);
9152 extra_headers
= NULL
;
9154 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9156 g_hash_table_destroy(compose
->email_hashtable
);
9158 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9159 compose
->folder_update_callback_id
);
9161 procmsg_msginfo_free(&(compose
->targetinfo
));
9162 procmsg_msginfo_free(&(compose
->replyinfo
));
9163 procmsg_msginfo_free(&(compose
->fwdinfo
));
9165 g_free(compose
->replyto
);
9166 g_free(compose
->cc
);
9167 g_free(compose
->bcc
);
9168 g_free(compose
->newsgroups
);
9169 g_free(compose
->followup_to
);
9171 g_free(compose
->ml_post
);
9173 g_free(compose
->inreplyto
);
9174 g_free(compose
->references
);
9175 g_free(compose
->msgid
);
9176 g_free(compose
->boundary
);
9178 g_free(compose
->redirect_filename
);
9179 if (compose
->undostruct
)
9180 undo_destroy(compose
->undostruct
);
9182 g_free(compose
->sig_str
);
9184 g_free(compose
->exteditor_file
);
9186 g_free(compose
->orig_charset
);
9188 g_free(compose
->privacy_system
);
9189 g_free(compose
->encdata
);
9191 #ifndef USE_ALT_ADDRBOOK
9192 if (addressbook_get_target_compose() == compose
)
9193 addressbook_set_target_compose(NULL
);
9196 if (compose
->gtkaspell
) {
9197 gtkaspell_delete(compose
->gtkaspell
);
9198 compose
->gtkaspell
= NULL
;
9202 if (!compose
->batch
) {
9203 gtk_window_get_size(GTK_WINDOW(compose
->window
),
9204 &allocation
.width
, &allocation
.height
);
9205 prefs_common
.compose_width
= allocation
.width
;
9206 prefs_common
.compose_height
= allocation
.height
;
9209 if (!gtk_widget_get_parent(compose
->paned
))
9210 gtk_widget_destroy(compose
->paned
);
9211 gtk_widget_destroy(compose
->popupmenu
);
9213 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9214 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9215 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9217 message_search_close(compose
);
9218 gtk_widget_destroy(compose
->window
);
9219 toolbar_destroy(compose
->toolbar
);
9220 g_free(compose
->toolbar
);
9221 g_mutex_clear(&compose
->mutex
);
9225 static void compose_attach_info_free(AttachInfo
*ainfo
)
9227 g_free(ainfo
->file
);
9228 g_free(ainfo
->content_type
);
9229 g_free(ainfo
->name
);
9230 g_free(ainfo
->charset
);
9234 static void compose_attach_update_label(Compose
*compose
)
9239 GtkTreeModel
*model
;
9243 if (compose
== NULL
)
9246 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9247 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9248 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9252 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9253 total_size
= ainfo
->size
;
9254 while(gtk_tree_model_iter_next(model
, &iter
)) {
9255 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9256 total_size
+= ainfo
->size
;
9259 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9260 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9264 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9266 Compose
*compose
= (Compose
*)data
;
9267 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9268 GtkTreeSelection
*selection
;
9270 GtkTreeModel
*model
;
9272 selection
= gtk_tree_view_get_selection(tree_view
);
9273 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9274 cm_return_if_fail(sel
);
9276 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9277 GtkTreePath
*path
= cur
->data
;
9278 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9281 gtk_tree_path_free(path
);
9284 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9285 GtkTreeRowReference
*ref
= cur
->data
;
9286 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9289 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9290 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9292 gtk_tree_path_free(path
);
9293 gtk_tree_row_reference_free(ref
);
9297 compose_attach_update_label(compose
);
9300 static struct _AttachProperty
9303 GtkWidget
*mimetype_entry
;
9304 GtkWidget
*encoding_optmenu
;
9305 GtkWidget
*path_entry
;
9306 GtkWidget
*filename_entry
;
9308 GtkWidget
*cancel_btn
;
9311 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9313 gtk_tree_path_free((GtkTreePath
*)ptr
);
9316 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9318 Compose
*compose
= (Compose
*)data
;
9319 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9321 GtkComboBox
*optmenu
;
9322 GtkTreeSelection
*selection
;
9324 GtkTreeModel
*model
;
9327 static gboolean cancelled
;
9329 /* only if one selected */
9330 selection
= gtk_tree_view_get_selection(tree_view
);
9331 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9334 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9335 cm_return_if_fail(sel
);
9337 path
= (GtkTreePath
*) sel
->data
;
9338 gtk_tree_model_get_iter(model
, &iter
, path
);
9339 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9342 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9348 if (!attach_prop
.window
)
9349 compose_attach_property_create(&cancelled
);
9350 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9351 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9352 gtk_widget_show(attach_prop
.window
);
9353 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9354 GTK_WINDOW(compose
->window
));
9356 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9357 if (ainfo
->encoding
== ENC_UNKNOWN
)
9358 combobox_select_by_data(optmenu
, ENC_BASE64
);
9360 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9362 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9363 ainfo
->content_type
? ainfo
->content_type
: "");
9364 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9365 ainfo
->file
? ainfo
->file
: "");
9366 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9367 ainfo
->name
? ainfo
->name
: "");
9370 const gchar
*entry_text
;
9372 gchar
*cnttype
= NULL
;
9379 gtk_widget_hide(attach_prop
.window
);
9380 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9385 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9386 if (*entry_text
!= '\0') {
9389 text
= g_strstrip(g_strdup(entry_text
));
9390 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9391 cnttype
= g_strdup(text
);
9394 alertpanel_error(_("Invalid MIME type."));
9400 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9402 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9403 if (*entry_text
!= '\0') {
9404 if (is_file_exist(entry_text
) &&
9405 (size
= get_file_size(entry_text
)) > 0)
9406 file
= g_strdup(entry_text
);
9409 (_("File doesn't exist or is empty."));
9415 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9416 if (*entry_text
!= '\0') {
9417 g_free(ainfo
->name
);
9418 ainfo
->name
= g_strdup(entry_text
);
9422 g_free(ainfo
->content_type
);
9423 ainfo
->content_type
= cnttype
;
9426 g_free(ainfo
->file
);
9430 ainfo
->size
= (goffset
)size
;
9432 /* update tree store */
9433 text
= to_human_readable(ainfo
->size
);
9434 gtk_tree_model_get_iter(model
, &iter
, path
);
9435 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9436 COL_MIMETYPE
, ainfo
->content_type
,
9438 COL_NAME
, ainfo
->name
,
9439 COL_CHARSET
, ainfo
->charset
,
9445 gtk_tree_path_free(path
);
9448 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9450 label = gtk_label_new(str); \
9451 gtk_grid_attach(GTK_GRID(table), label, 0, top, 1, 1); \
9452 gtk_label_set_xalign(GTK_LABEL(label), 0.0); \
9453 entry = gtk_entry_new(); \
9454 gtk_grid_attach(GTK_GRID(table), entry, 1, top, 1, 1); \
9457 static void compose_attach_property_create(gboolean
*cancelled
)
9463 GtkWidget
*mimetype_entry
;
9466 GtkListStore
*optmenu_menu
;
9467 GtkWidget
*path_entry
;
9468 GtkWidget
*filename_entry
;
9471 GtkWidget
*cancel_btn
;
9472 GList
*mime_type_list
, *strlist
;
9475 debug_print("Creating attach_property window...\n");
9477 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9478 gtk_widget_set_size_request(window
, 480, -1);
9479 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9480 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9481 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9482 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9483 g_signal_connect(G_OBJECT(window
), "delete_event",
9484 G_CALLBACK(attach_property_delete_event
),
9486 g_signal_connect(G_OBJECT(window
), "key_press_event",
9487 G_CALLBACK(attach_property_key_pressed
),
9490 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
9491 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9493 table
= gtk_grid_new();
9494 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9495 gtk_grid_set_row_spacing(GTK_GRID(table
), 8);
9496 gtk_grid_set_column_spacing(GTK_GRID(table
), 8);
9498 label
= gtk_label_new(_("MIME type"));
9499 gtk_grid_attach(GTK_GRID(table
), label
, 0, 0, 1, 1);
9500 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9501 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9502 gtk_grid_attach(GTK_GRID(table
), mimetype_entry
, 1, 0, 1, 1);
9503 gtk_widget_set_hexpand(mimetype_entry
, TRUE
);
9504 gtk_widget_set_halign(mimetype_entry
, GTK_ALIGN_FILL
);
9506 /* stuff with list */
9507 mime_type_list
= procmime_get_mime_type_list();
9509 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9510 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9513 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9515 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9518 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9519 (GCompareFunc
)g_strcmp0
);
9522 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9523 mime_type_list
= mime_type_list
->next
) {
9524 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9525 g_free(mime_type_list
->data
);
9527 g_list_free(strlist
);
9528 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9529 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9531 label
= gtk_label_new(_("Encoding"));
9532 gtk_grid_attach(GTK_GRID(table
), label
, 0, 1, 1, 1);
9533 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9535 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
9536 gtk_grid_attach(GTK_GRID(table
), hbox
, 1, 1, 1, 1);
9537 gtk_widget_set_hexpand(hbox
, TRUE
);
9538 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
9540 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9541 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9543 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9544 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9545 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9546 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9547 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9549 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9551 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9552 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9554 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, NULL
, _("_Cancel"),
9555 &ok_btn
, NULL
, _("_OK"),
9557 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9558 gtk_widget_grab_default(ok_btn
);
9560 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9561 G_CALLBACK(attach_property_ok
),
9563 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9564 G_CALLBACK(attach_property_cancel
),
9567 gtk_widget_show_all(vbox
);
9569 attach_prop
.window
= window
;
9570 attach_prop
.mimetype_entry
= mimetype_entry
;
9571 attach_prop
.encoding_optmenu
= optmenu
;
9572 attach_prop
.path_entry
= path_entry
;
9573 attach_prop
.filename_entry
= filename_entry
;
9574 attach_prop
.ok_btn
= ok_btn
;
9575 attach_prop
.cancel_btn
= cancel_btn
;
9578 #undef SET_LABEL_AND_ENTRY
9580 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9586 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9592 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9593 gboolean
*cancelled
)
9601 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9603 gboolean
*cancelled
)
9605 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9609 if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
||
9610 event
->keyval
== GDK_KEY_Return
)) {
9618 static gboolean
compose_can_autosave(Compose
*compose
)
9620 if (compose
->privacy_system
&& compose
->use_encryption
)
9621 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9623 return prefs_common
.autosave
;
9627 * compose_exec_ext_editor:
9629 * Open (and optionally embed) external editor
9631 static void compose_exec_ext_editor(Compose
*compose
)
9636 Window socket_wid
= 0;
9638 #endif /* G_OS_WIN32 */
9640 GError
*error
= NULL
;
9644 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9645 G_DIR_SEPARATOR
, compose
);
9647 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9648 alertpanel_error(_("Could not write the body to file:\n%s"),
9654 if (compose_get_ext_editor_uses_socket()) {
9656 /* Only allow one socket */
9657 if (compose
->exteditor_socket
!= NULL
) {
9658 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9659 /* Move the focus off of the socket */
9660 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9665 /* Create the receiving GtkSocket */
9666 socket
= gtk_socket_new ();
9667 g_signal_connect (G_OBJECT(socket
), "plug-removed",
9668 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9670 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9671 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9672 /* Realize the socket so that we can use its ID */
9673 gtk_widget_realize(socket
);
9674 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9675 compose
->exteditor_socket
= socket
;
9677 alertpanel_error(_("Socket communication with an external editor is not available on Windows."));
9680 #endif /* G_OS_WIN32 */
9683 if (compose_get_ext_editor_cmd_valid()) {
9684 if (compose_get_ext_editor_uses_socket()) {
9686 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9687 s
= strstr(p
, "%w");
9689 if (strstr(p
, "%s") < s
)
9690 cmd
= g_strdup_printf(p
, tmp
, socket_wid
);
9692 cmd
= g_strdup_printf(p
, socket_wid
, tmp
);
9694 #endif /* G_OS_WIN32 */
9696 cmd
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), tmp
);
9699 if (prefs_common_get_ext_editor_cmd())
9700 g_warning("external editor command-line is invalid: '%s'",
9701 prefs_common_get_ext_editor_cmd());
9702 cmd
= g_strdup_printf(DEFAULT_EDITOR_CMD
, tmp
);
9705 argv
= strsplit_with_quote(cmd
, " ", 0);
9707 if (!g_spawn_async(NULL
, argv
, NULL
,
9708 G_SPAWN_DO_NOT_REAP_CHILD
| G_SPAWN_SEARCH_PATH
,
9709 NULL
, NULL
, &pid
, &error
)) {
9710 alertpanel_error(_("Could not spawn the following "
9711 "external editor command:\n%s\n%s"),
9712 cmd
, error
? error
->message
: _("Unknown error"));
9714 g_error_free(error
);
9723 compose
->exteditor_file
= g_strdup(tmp
);
9724 compose
->exteditor_pid
= pid
;
9725 compose
->exteditor_tag
= g_child_watch_add(pid
,
9726 compose_ext_editor_closed_cb
,
9729 compose_set_ext_editor_sensitive(compose
, FALSE
);
9735 * compose_ext_editor_cb:
9737 * External editor has closed (called by g_child_watch)
9739 static void compose_ext_editor_closed_cb(GPid pid
, gint exit_status
, gpointer data
)
9741 Compose
*compose
= (Compose
*)data
;
9742 GError
*error
= NULL
;
9743 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9744 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9745 GtkTextIter start
, end
;
9748 #if GLIB_CHECK_VERSION(2,70,0)
9749 if (!g_spawn_check_wait_status(exit_status
, &error
)) {
9751 if (!g_spawn_check_exit_status(exit_status
, &error
)) {
9754 _("External editor stopped with an error: %s"),
9755 error
? error
->message
: _("Unknown error"));
9757 g_error_free(error
);
9759 g_spawn_close_pid(compose
->exteditor_pid
);
9761 gtk_text_buffer_set_text(buffer
, "", -1);
9762 compose_insert_file(compose
, compose
->exteditor_file
);
9763 compose_changed_cb(NULL
, compose
);
9765 /* Check if we should save the draft or not */
9766 if (compose_can_autosave(compose
))
9767 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9769 if (claws_unlink(compose
->exteditor_file
) < 0)
9770 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9772 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9773 gtk_text_buffer_get_start_iter(buffer
, &start
);
9774 gtk_text_buffer_get_end_iter(buffer
, &end
);
9775 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9776 if (chars
&& strlen(chars
) > 0)
9777 compose
->modified
= TRUE
;
9780 compose_set_ext_editor_sensitive(compose
, TRUE
);
9782 g_free(compose
->exteditor_file
);
9783 compose
->exteditor_file
= NULL
;
9784 compose
->exteditor_pid
= INVALID_PID
;
9785 compose
->exteditor_tag
= -1;
9786 if (compose
->exteditor_socket
) {
9787 gtk_widget_destroy(compose
->exteditor_socket
);
9788 compose
->exteditor_socket
= NULL
;
9793 static gboolean
compose_get_ext_editor_cmd_valid()
9795 gboolean has_s
= FALSE
;
9796 gboolean has_w
= FALSE
;
9797 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9800 while ((p
= strchr(p
, '%'))) {
9806 } else if (*p
== 'w') {
9817 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9819 GPid pid
= compose
->exteditor_pid
;
9820 gchar
*pidmsg
= NULL
;
9826 pidmsg
= g_strdup_printf
9827 #if GLIB_CHECK_VERSION(2, 50, 0)
9828 (_("process id: %" G_PID_FORMAT
),
9830 (_("process id: %d"),
9834 msg
= g_strdup_printf
9835 (_("The external editor is still working.\n"
9836 "Force terminating the process?\n"
9838 val
= alertpanel_full(_("Notice"), msg
, NULL
, _("_No"), NULL
, _("_Yes"),
9839 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9843 if (val
== G_ALERTALTERNATE
) {
9844 g_source_remove(compose
->exteditor_tag
);
9847 if (!TerminateProcess(compose
->exteditor_pid
, 0))
9848 perror("TerminateProcess");
9850 if (kill(pid
, SIGTERM
) < 0) perror("kill");
9851 waitpid(compose
->exteditor_pid
, NULL
, 0);
9852 #endif /* G_OS_WIN32 */
9854 g_warning("terminated %s, temporary file: %s",
9855 pidmsg
, compose
->exteditor_file
);
9856 g_spawn_close_pid(compose
->exteditor_pid
);
9858 compose_set_ext_editor_sensitive(compose
, TRUE
);
9860 g_free(compose
->exteditor_file
);
9861 compose
->exteditor_file
= NULL
;
9862 compose
->exteditor_pid
= INVALID_PID
;
9863 compose
->exteditor_tag
= -1;
9875 static char *ext_editor_menu_entries
[] = {
9876 "Menu/Message/Send",
9877 "Menu/Message/SendLater",
9878 "Menu/Message/InsertFile",
9879 "Menu/Message/InsertSig",
9880 "Menu/Message/ReplaceSig",
9881 "Menu/Message/Save",
9882 "Menu/Message/Print",
9887 "Menu/Tools/ShowRuler",
9888 "Menu/Tools/Actions",
9893 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9898 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9899 cm_menu_set_sensitive_full(compose
->ui_manager
,
9900 ext_editor_menu_entries
[i
], sensitive
);
9903 if (compose_get_ext_editor_uses_socket()) {
9905 if (compose
->exteditor_socket
)
9906 gtk_widget_hide(compose
->exteditor_socket
);
9907 gtk_widget_show(compose
->scrolledwin
);
9908 if (prefs_common
.show_ruler
)
9909 gtk_widget_show(compose
->ruler_hbox
);
9910 /* Fix the focus, as it doesn't go anywhere when the
9911 * socket is hidden or destroyed */
9912 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9914 g_assert (compose
->exteditor_socket
!= NULL
);
9915 /* Fix the focus, as it doesn't go anywhere when the
9916 * edit box is hidden */
9917 if (gtk_widget_is_focus(compose
->text
))
9918 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9919 gtk_widget_hide(compose
->scrolledwin
);
9920 gtk_widget_hide(compose
->ruler_hbox
);
9921 gtk_widget_show(compose
->exteditor_socket
);
9924 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9926 if (compose
->toolbar
->send_btn
)
9927 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9928 if (compose
->toolbar
->sendl_btn
)
9929 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9930 if (compose
->toolbar
->draft_btn
)
9931 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9932 if (compose
->toolbar
->insert_btn
)
9933 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9934 if (compose
->toolbar
->sig_btn
)
9935 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9936 if (compose
->toolbar
->exteditor_btn
)
9937 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9938 if (compose
->toolbar
->linewrap_current_btn
)
9939 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9940 if (compose
->toolbar
->linewrap_all_btn
)
9941 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9944 static gboolean
compose_get_ext_editor_uses_socket()
9946 return (prefs_common_get_ext_editor_cmd() &&
9947 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9951 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9953 compose
->exteditor_socket
= NULL
;
9954 /* returning FALSE allows destruction of the socket */
9957 #endif /* G_OS_WIN32 */
9960 * compose_undo_state_changed:
9962 * Change the sensivity of the menuentries undo and redo
9964 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9965 gint redo_state
, gpointer data
)
9967 Compose
*compose
= (Compose
*)data
;
9969 switch (undo_state
) {
9970 case UNDO_STATE_TRUE
:
9971 if (!undostruct
->undo_state
) {
9972 undostruct
->undo_state
= TRUE
;
9973 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9976 case UNDO_STATE_FALSE
:
9977 if (undostruct
->undo_state
) {
9978 undostruct
->undo_state
= FALSE
;
9979 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9982 case UNDO_STATE_UNCHANGED
:
9984 case UNDO_STATE_REFRESH
:
9985 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9988 g_warning("undo state not recognized");
9992 switch (redo_state
) {
9993 case UNDO_STATE_TRUE
:
9994 if (!undostruct
->redo_state
) {
9995 undostruct
->redo_state
= TRUE
;
9996 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9999 case UNDO_STATE_FALSE
:
10000 if (undostruct
->redo_state
) {
10001 undostruct
->redo_state
= FALSE
;
10002 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
10005 case UNDO_STATE_UNCHANGED
:
10007 case UNDO_STATE_REFRESH
:
10008 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10011 g_warning("redo state not recognized");
10016 /* callback functions */
10018 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10019 GtkAllocation
*allocation
,
10022 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10025 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10026 * includes "non-client" (windows-izm) in calculation, so this calculation
10027 * may not be accurate.
10029 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10030 GtkAllocation
*allocation
,
10031 GtkSHRuler
*shruler
)
10033 if (prefs_common
.show_ruler
) {
10034 gint char_width
= 0, char_height
= 0;
10035 gint line_width_in_chars
;
10037 gtkut_get_font_size(GTK_WIDGET(widget
),
10038 &char_width
, &char_height
);
10039 line_width_in_chars
=
10040 (allocation
->width
- allocation
->x
) / char_width
;
10042 /* got the maximum */
10043 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10044 0.0, line_width_in_chars
, 0);
10053 ComposePrefType type
;
10054 gboolean entry_marked
;
10055 } HeaderEntryState
;
10057 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10059 Compose
*compose
= (Compose
*)data
;
10062 gchar
*folderidentifier
;
10063 gint account_id
= 0;
10064 GtkTreeModel
*menu
;
10066 GSList
*list
, *saved_list
= NULL
;
10067 HeaderEntryState
*state
;
10069 /* Get ID of active account in the combo box */
10070 menu
= gtk_combo_box_get_model(optmenu
);
10071 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10072 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10074 ac
= account_find_from_id(account_id
);
10075 cm_return_if_fail(ac
!= NULL
);
10077 if (ac
!= compose
->account
) {
10078 compose_select_account(compose
, ac
, FALSE
);
10080 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10081 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10083 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10084 compose_destroy_headerentry(compose
, hentry
);
10087 state
= g_malloc0(sizeof(HeaderEntryState
));
10088 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10089 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10090 state
->entry
= gtk_editable_get_chars(
10091 GTK_EDITABLE(hentry
->entry
), 0, -1);
10092 state
->type
= hentry
->type
;
10094 saved_list
= g_slist_append(saved_list
, state
);
10095 compose_destroy_headerentry(compose
, hentry
);
10098 compose
->header_last
= NULL
;
10099 g_slist_free(compose
->header_list
);
10100 compose
->header_list
= NULL
;
10101 compose
->header_nextrow
= 1;
10102 compose_create_header_entry(compose
);
10104 if (ac
->set_autocc
&& ac
->auto_cc
)
10105 compose_entry_append(compose
, ac
->auto_cc
,
10106 COMPOSE_CC
, PREF_ACCOUNT
);
10107 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10108 compose_entry_append(compose
, ac
->auto_bcc
,
10109 COMPOSE_BCC
, PREF_ACCOUNT
);
10110 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10111 compose_entry_append(compose
, ac
->auto_replyto
,
10112 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10114 for (list
= saved_list
; list
; list
= list
->next
) {
10115 state
= (HeaderEntryState
*) list
->data
;
10117 compose_add_header_entry(compose
, state
->header
,
10118 state
->entry
, state
->type
);
10120 g_free(state
->header
);
10121 g_free(state
->entry
);
10124 g_slist_free(saved_list
);
10126 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10127 (ac
->protocol
== A_NNTP
) ?
10128 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10131 /* Set message save folder */
10132 compose_set_save_to(compose
, NULL
);
10133 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10134 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10135 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10136 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10137 compose_set_save_to(compose
, folderidentifier
);
10138 g_free(folderidentifier
);
10139 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10140 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10141 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10143 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10144 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10145 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10146 (compose
->account
, F_OUTBOX
));
10147 compose_set_save_to(compose
, folderidentifier
);
10148 g_free(folderidentifier
);
10152 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10153 GtkTreeViewColumn
*column
, Compose
*compose
)
10155 compose_attach_property(NULL
, compose
);
10158 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10161 Compose
*compose
= (Compose
*)data
;
10162 GtkTreeSelection
*attach_selection
;
10163 gint attach_nr_selected
;
10166 if (!event
) return FALSE
;
10168 if (event
->button
== 3) {
10169 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10170 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10172 /* If no rows, or just one row is selected, right-click should
10173 * open menu relevant to the row being right-clicked on. We
10174 * achieve that by selecting the clicked row first. If more
10175 * than one row is selected, we shouldn't modify the selection,
10176 * as user may want to remove selected rows (attachments). */
10177 if (attach_nr_selected
< 2) {
10178 gtk_tree_selection_unselect_all(attach_selection
);
10179 attach_nr_selected
= 0;
10180 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10181 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10182 if (path
!= NULL
) {
10183 gtk_tree_selection_select_path(attach_selection
, path
);
10184 gtk_tree_path_free(path
);
10185 attach_nr_selected
++;
10189 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10190 /* Properties menu item makes no sense with more than one row
10191 * selected, the properties dialog can only edit one attachment. */
10192 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10194 gtk_menu_popup_at_pointer(GTK_MENU(compose
->popupmenu
), NULL
);
10202 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10205 Compose
*compose
= (Compose
*)data
;
10207 if (!event
) return FALSE
;
10209 switch (event
->keyval
) {
10210 case GDK_KEY_Delete
:
10211 compose_attach_remove_selected(NULL
, compose
);
10217 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10219 toolbar_comp_set_sensitive(compose
, allow
);
10220 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10221 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10223 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10225 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10226 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10227 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10229 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10233 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10235 Compose
*compose
= (Compose
*)data
;
10238 if (compose
->exteditor_tag
!= -1) {
10239 debug_print("ignoring send: external editor still open\n");
10243 if (prefs_common
.work_offline
&&
10244 !inc_offline_should_override(TRUE
,
10245 _("Claws Mail needs network access in order "
10246 "to send this email.")))
10249 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10250 g_source_remove(compose
->draft_timeout_tag
);
10251 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10254 compose_send(compose
);
10257 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10259 Compose
*compose
= (Compose
*)data
;
10260 ComposeQueueResult val
;
10263 compose_allow_user_actions(compose
, FALSE
);
10264 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10265 compose_allow_user_actions(compose
, TRUE
);
10268 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10269 compose_close(compose
);
10271 _display_queue_error(val
);
10274 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10277 #define DRAFTED_AT_EXIT "drafted_at_exit"
10278 static void compose_register_draft(MsgInfo
*info
)
10280 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10281 DRAFTED_AT_EXIT
, NULL
);
10282 FILE *fp
= claws_fopen(filepath
, "ab");
10285 gchar
*name
= folder_item_get_identifier(info
->folder
);
10286 fprintf(fp
, "%s\t%d\n", name
, info
->msgnum
);
10294 gboolean
compose_draft (gpointer data
, guint action
)
10296 Compose
*compose
= (Compose
*)data
;
10298 FolderItemPrefs
*prefs
;
10302 MsgFlags flag
= {0, 0};
10303 static gboolean lock
= FALSE
;
10304 MsgInfo
*newmsginfo
;
10306 gboolean target_locked
= FALSE
;
10307 gboolean err
= FALSE
;
10310 if (lock
) return FALSE
;
10312 if (compose
->sending
)
10315 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10316 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10318 if (!g_mutex_trylock(&compose
->mutex
)) {
10319 /* we don't want to lock the mutex once it's available,
10320 * because as the only other part of compose.c locking
10321 * it is compose_close - which means once unlocked,
10322 * the compose struct will be freed */
10323 debug_print("couldn't lock mutex, probably sending\n");
10329 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10330 G_DIR_SEPARATOR
, compose
);
10331 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10332 FILE_OP_ERROR(tmp
, "claws_fopen");
10336 /* chmod for security unless folder chmod is set */
10337 prefs
= draft
->prefs
;
10338 if (prefs
&& prefs
->enable_folder_chmod
&& prefs
->folder_chmod
) {
10339 filemode
= prefs
->folder_chmod
;
10340 if (filemode
& S_IRGRP
) filemode
|= S_IWGRP
;
10341 if (filemode
& S_IROTH
) filemode
|= S_IWOTH
;
10342 if (chmod(tmp
, filemode
) < 0)
10343 FILE_OP_ERROR(tmp
, "chmod");
10344 } else if (change_file_mode_rw(fp
, tmp
) < 0) {
10345 FILE_OP_ERROR(tmp
, "chmod");
10346 g_warning("can't change file mode");
10349 /* Save draft infos */
10350 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10351 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10353 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10354 gchar
*savefolderid
;
10356 savefolderid
= compose_get_save_to(compose
);
10357 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10358 g_free(savefolderid
);
10360 if (compose
->return_receipt
) {
10361 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10363 if (compose
->privacy_system
) {
10364 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10365 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10366 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10369 /* Message-ID of message replying to */
10370 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10371 gchar
*folderid
= NULL
;
10373 if (compose
->replyinfo
->folder
)
10374 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10375 if (folderid
== NULL
)
10376 folderid
= g_strdup("NULL");
10378 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10381 /* Message-ID of message forwarding to */
10382 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10383 gchar
*folderid
= NULL
;
10385 if (compose
->fwdinfo
->folder
)
10386 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10387 if (folderid
== NULL
)
10388 folderid
= g_strdup("NULL");
10390 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10394 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10395 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10397 sheaders
= compose_get_manual_headers_info(compose
);
10398 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10401 /* end of headers */
10402 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10409 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10413 if (claws_safe_fclose(fp
) == EOF
) {
10417 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10418 if (compose
->targetinfo
) {
10419 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10421 flag
.perm_flags
|= MSG_LOCKED
;
10423 flag
.tmp_flags
= MSG_DRAFT
;
10425 folder_item_scan(draft
);
10426 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10427 MsgInfo
*tmpinfo
= NULL
;
10428 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10429 if (compose
->msgid
) {
10430 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10433 msgnum
= tmpinfo
->msgnum
;
10434 procmsg_msginfo_free(&tmpinfo
);
10435 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10437 debug_print("didn't get draft msgnum after scanning\n");
10440 debug_print("got draft msgnum %d from adding\n", msgnum
);
10446 if (action
!= COMPOSE_AUTO_SAVE
) {
10447 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10448 alertpanel_error(_("Could not save draft."));
10451 gtkut_window_popup(compose
->window
);
10452 val
= alertpanel_full(_("Could not save draft"),
10453 _("Could not save draft.\n"
10454 "Do you want to cancel exit or discard this email?"),
10455 NULL
, _("_Cancel exit"), NULL
, _("_Discard email"),
10456 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_QUESTION
);
10457 if (val
== G_ALERTALTERNATE
) {
10459 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10460 compose_close(compose
);
10464 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10473 if (compose
->mode
== COMPOSE_REEDIT
) {
10474 compose_remove_reedit_target(compose
, TRUE
);
10477 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10480 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10482 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10484 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10485 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10486 procmsg_msginfo_set_flags(newmsginfo
, 0,
10487 MSG_HAS_ATTACHMENT
);
10489 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10490 compose_register_draft(newmsginfo
);
10492 procmsg_msginfo_free(&newmsginfo
);
10495 folder_item_scan(draft
);
10497 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10499 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10500 compose_close(compose
);
10507 GError
*error
= NULL
;
10512 goffset size
, mtime
;
10514 path
= folder_item_fetch_msg(draft
, msgnum
);
10515 if (path
== NULL
) {
10516 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10520 f
= g_file_new_for_path(path
);
10521 fi
= g_file_query_info(f
, "standard::size,time::modified",
10522 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10523 if (error
!= NULL
) {
10524 debug_print("couldn't query file info for '%s': %s\n",
10525 path
, error
->message
);
10526 g_error_free(error
);
10531 size
= g_file_info_get_size(fi
);
10532 g_file_info_get_modification_time(fi
, &tv
);
10534 g_object_unref(fi
);
10537 if (g_stat(path
, &s
) < 0) {
10538 FILE_OP_ERROR(path
, "stat");
10543 mtime
= s
.st_mtime
;
10547 procmsg_msginfo_free(&(compose
->targetinfo
));
10548 compose
->targetinfo
= procmsg_msginfo_new();
10549 compose
->targetinfo
->msgnum
= msgnum
;
10550 compose
->targetinfo
->size
= size
;
10551 compose
->targetinfo
->mtime
= mtime
;
10552 compose
->targetinfo
->folder
= draft
;
10554 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10555 compose
->mode
= COMPOSE_REEDIT
;
10557 if (action
== COMPOSE_AUTO_SAVE
) {
10558 compose
->modified
= FALSE
;
10559 compose
->autosaved_draft
= compose
->targetinfo
;
10561 compose_set_title(compose
);
10565 g_mutex_unlock(&compose
->mutex
);
10569 void compose_clear_exit_drafts(void)
10571 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10572 DRAFTED_AT_EXIT
, NULL
);
10573 if (is_file_exist(filepath
))
10574 claws_unlink(filepath
);
10579 void compose_reopen_exit_drafts(void)
10581 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10582 DRAFTED_AT_EXIT
, NULL
);
10583 FILE *fp
= claws_fopen(filepath
, "rb");
10587 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10588 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10589 const gchar
*folder
= parts
[0];
10590 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10592 if (folder
&& *folder
&& msgnum
> -1) {
10593 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10594 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10596 compose_reedit(info
, FALSE
);
10603 compose_clear_exit_drafts();
10606 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10608 Compose
*compose
= (Compose
*)data
;
10609 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10610 compose
->rmode
= COMPOSE_REEDIT
;
10611 compose
->modified
= FALSE
;
10612 compose_set_title(compose
);
10615 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10617 if (compose
&& file_list
) {
10620 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10621 gchar
*file
= (gchar
*) tmp
->data
;
10622 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10623 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10624 compose_changed_cb(NULL
, compose
);
10629 g_free(utf8_filename
);
10634 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10636 Compose
*compose
= (Compose
*)data
;
10639 if (compose
->redirect_filename
!= NULL
)
10642 /* Set focus_window properly, in case we were called via popup menu,
10643 * which unsets it (via focus_out_event callback on compose window). */
10644 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10646 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10649 compose_attach_from_list(compose
, file_list
, TRUE
);
10650 g_list_free(file_list
);
10654 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10656 Compose
*compose
= (Compose
*)data
;
10658 gint files_inserted
= 0;
10660 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10665 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10666 gchar
*file
= (gchar
*) tmp
->data
;
10667 gchar
*filedup
= g_strdup(file
);
10668 gchar
*shortfile
= g_path_get_basename(filedup
);
10669 ComposeInsertResult res
;
10670 /* insert the file if the file is short or if the user confirmed that
10671 he/she wants to insert the large file */
10672 res
= compose_insert_file(compose
, file
);
10673 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10674 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10675 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10676 alertpanel_error(_("File '%s' contained invalid characters\n"
10677 "for the current encoding, insertion may be incorrect."),
10679 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10686 g_list_free(file_list
);
10690 if (files_inserted
> 0 && compose
->gtkaspell
&&
10691 compose
->gtkaspell
->check_while_typing
)
10692 gtkaspell_highlight_all(compose
->gtkaspell
);
10696 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10698 Compose
*compose
= (Compose
*)data
;
10700 compose_insert_sig(compose
, FALSE
);
10703 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10705 Compose
*compose
= (Compose
*)data
;
10707 compose_insert_sig(compose
, TRUE
);
10710 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10714 Compose
*compose
= (Compose
*)data
;
10716 gtk_window_get_position(GTK_WINDOW(widget
), &x
, &y
);
10717 if (!compose
->batch
) {
10718 prefs_common
.compose_x
= x
;
10719 prefs_common
.compose_y
= y
;
10721 if (compose
->sending
|| compose
->updating
)
10723 compose_close_cb(NULL
, compose
);
10727 void compose_close_toolbar(Compose
*compose
)
10729 compose_close_cb(NULL
, compose
);
10732 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10734 Compose
*compose
= (Compose
*)data
;
10737 if (compose
->exteditor_tag
!= -1) {
10738 if (!compose_ext_editor_kill(compose
))
10742 if (compose
->modified
) {
10743 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10744 if (!g_mutex_trylock(&compose
->mutex
)) {
10745 /* we don't want to lock the mutex once it's available,
10746 * because as the only other part of compose.c locking
10747 * it is compose_close - which means once unlocked,
10748 * the compose struct will be freed */
10749 debug_print("couldn't lock mutex, probably sending\n");
10752 if (!reedit
|| (compose
->folder
!= NULL
&& compose
->folder
->stype
== F_DRAFT
)) {
10753 val
= alertpanel(_("Discard message"),
10754 _("This message has been modified. Discard it?"),
10755 NULL
, _("_Discard"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10758 val
= alertpanel(_("Save changes"),
10759 _("This message has been modified. Save the latest changes?"),
10760 NULL
, _("_Don't save"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10761 ALERTFOCUS_SECOND
);
10763 g_mutex_unlock(&compose
->mutex
);
10765 case G_ALERTDEFAULT
:
10766 if (compose_can_autosave(compose
) && !reedit
)
10767 compose_remove_draft(compose
);
10769 case G_ALERTALTERNATE
:
10770 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10777 compose_close(compose
);
10780 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10782 Compose
*compose
= (Compose
*) data
;
10784 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10785 if (compose
->targetinfo
)
10786 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10789 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10791 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10792 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10793 Compose
*compose
= (Compose
*) data
;
10796 compose
->out_encoding
= (CharSet
)value
;
10799 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10801 Compose
*compose
= (Compose
*)data
;
10803 #ifndef USE_ALT_ADDRBOOK
10804 addressbook_open(compose
);
10806 GError
* error
= NULL
;
10807 addressbook_connect_signals(compose
);
10808 addressbook_dbus_open(TRUE
, &error
);
10810 g_warning("%s", error
->message
);
10811 g_error_free(error
);
10816 static void about_show_cb(GtkAction
*action
, gpointer data
)
10821 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10823 Compose
*compose
= (Compose
*)data
;
10828 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10829 cm_return_if_fail(tmpl
!= NULL
);
10831 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10833 val
= alertpanel(_("Apply template"), msg
,
10834 NULL
, _("_Replace"), NULL
, _("_Insert"), NULL
, _("_Cancel"),
10838 if (val
== G_ALERTDEFAULT
)
10839 compose_template_apply(compose
, tmpl
, TRUE
);
10840 else if (val
== G_ALERTALTERNATE
)
10841 compose_template_apply(compose
, tmpl
, FALSE
);
10844 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10846 Compose
*compose
= (Compose
*)data
;
10848 if (compose
->exteditor_tag
!= -1) {
10849 debug_print("ignoring open external editor: external editor still open\n");
10852 compose_exec_ext_editor(compose
);
10855 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10857 Compose
*compose
= (Compose
*)data
;
10858 gboolean prev_autowrap
= compose
->autowrap
;
10860 compose
->autowrap
= FALSE
;
10861 undo_undo(compose
->undostruct
);
10862 compose
->autowrap
= prev_autowrap
;
10865 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10867 Compose
*compose
= (Compose
*)data
;
10868 gboolean prev_autowrap
= compose
->autowrap
;
10870 compose
->autowrap
= FALSE
;
10871 undo_redo(compose
->undostruct
);
10872 compose
->autowrap
= prev_autowrap
;
10875 static void entry_cut_clipboard(GtkWidget
*entry
)
10877 if (GTK_IS_EDITABLE(entry
))
10878 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10879 else if (GTK_IS_TEXT_VIEW(entry
))
10880 gtk_text_buffer_cut_clipboard(
10881 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10882 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10886 static void entry_copy_clipboard(GtkWidget
*entry
)
10888 if (GTK_IS_EDITABLE(entry
))
10889 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10890 else if (GTK_IS_TEXT_VIEW(entry
))
10891 gtk_text_buffer_copy_clipboard(
10892 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10893 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10896 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10897 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10899 if (GTK_IS_TEXT_VIEW(entry
)) {
10900 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10901 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10902 GtkTextIter start_iter
, end_iter
;
10904 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10906 if (contents
== NULL
)
10909 glong len
= g_utf8_strlen(contents
, -1);
10910 if (len
> MAX_ALLOCA_MEM_SIZE
) {
10911 alertpanel_error(_("Size of pasted text exceeds limit (%dKiB) for paste.\n"
10912 "Attach as file instead."),
10913 (MAX_ALLOCA_MEM_SIZE
/ 1024));
10916 /* we shouldn't delete the selection when middle-click-pasting, or we
10917 * can't mid-click-paste our own selection */
10918 if (clip
!= GDK_SELECTION_PRIMARY
) {
10919 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10920 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10923 if (insert_place
== NULL
) {
10924 /* if insert_place isn't specified, insert at the cursor.
10925 * used for Ctrl-V pasting */
10926 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10927 start
= gtk_text_iter_get_offset(&start_iter
);
10928 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10930 /* if insert_place is specified, paste here.
10931 * used for mid-click-pasting */
10932 start
= gtk_text_iter_get_offset(insert_place
);
10933 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10934 if (prefs_common
.primary_paste_unselects
)
10935 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10939 /* paste unwrapped: mark the paste so it's not wrapped later */
10940 end
= start
+ strlen(contents
);
10941 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10942 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10943 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10944 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10945 /* rewrap paragraph now (after a mid-click-paste) */
10946 mark_start
= gtk_text_buffer_get_insert(buffer
);
10947 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10948 gtk_text_iter_backward_char(&start_iter
);
10949 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10951 } else if (GTK_IS_EDITABLE(entry
))
10952 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10954 compose
->modified
= TRUE
;
10957 static void entry_allsel(GtkWidget
*entry
)
10959 if (GTK_IS_EDITABLE(entry
))
10960 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10961 else if (GTK_IS_TEXT_VIEW(entry
)) {
10962 GtkTextIter startiter
, enditer
;
10963 GtkTextBuffer
*textbuf
;
10965 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10966 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10967 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10969 gtk_text_buffer_move_mark_by_name(textbuf
,
10970 "selection_bound", &startiter
);
10971 gtk_text_buffer_move_mark_by_name(textbuf
,
10972 "insert", &enditer
);
10976 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10978 Compose
*compose
= (Compose
*)data
;
10979 if (compose
->focused_editable
10980 #ifndef GENERIC_UMPC
10981 && gtk_widget_has_focus(compose
->focused_editable
)
10984 entry_cut_clipboard(compose
->focused_editable
);
10987 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10989 Compose
*compose
= (Compose
*)data
;
10990 if (compose
->focused_editable
10991 #ifndef GENERIC_UMPC
10992 && gtk_widget_has_focus(compose
->focused_editable
)
10995 entry_copy_clipboard(compose
->focused_editable
);
10998 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
11000 Compose
*compose
= (Compose
*)data
;
11001 gint prev_autowrap
;
11002 GtkTextBuffer
*buffer
;
11004 if (compose
->focused_editable
11005 #ifndef GENERIC_UMPC
11006 && gtk_widget_has_focus(compose
->focused_editable
)
11009 entry_paste_clipboard(compose
, compose
->focused_editable
,
11010 prefs_common
.linewrap_pastes
,
11011 GDK_SELECTION_CLIPBOARD
, NULL
);
11016 #ifndef GENERIC_UMPC
11017 gtk_widget_has_focus(compose
->text
) &&
11019 compose
->gtkaspell
&&
11020 compose
->gtkaspell
->check_while_typing
)
11021 gtkaspell_highlight_all(compose
->gtkaspell
);
11025 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11027 Compose
*compose
= (Compose
*)data
;
11028 gint wrap_quote
= prefs_common
.linewrap_quote
;
11029 if (compose
->focused_editable
11030 #ifndef GENERIC_UMPC
11031 && gtk_widget_has_focus(compose
->focused_editable
)
11034 /* let text_insert() (called directly or at a later time
11035 * after the gtk_editable_paste_clipboard) know that
11036 * text is to be inserted as a quotation. implemented
11037 * by using a simple refcount... */
11038 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11039 G_OBJECT(compose
->focused_editable
),
11040 "paste_as_quotation"));
11041 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11042 "paste_as_quotation",
11043 GINT_TO_POINTER(paste_as_quotation
+ 1));
11044 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11045 entry_paste_clipboard(compose
, compose
->focused_editable
,
11046 prefs_common
.linewrap_pastes
,
11047 GDK_SELECTION_CLIPBOARD
, NULL
);
11048 prefs_common
.linewrap_quote
= wrap_quote
;
11052 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11054 Compose
*compose
= (Compose
*)data
;
11055 gint prev_autowrap
;
11056 GtkTextBuffer
*buffer
;
11058 if (compose
->focused_editable
11059 #ifndef GENERIC_UMPC
11060 && gtk_widget_has_focus(compose
->focused_editable
)
11063 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11064 GDK_SELECTION_CLIPBOARD
, NULL
);
11069 #ifndef GENERIC_UMPC
11070 gtk_widget_has_focus(compose
->text
) &&
11072 compose
->gtkaspell
&&
11073 compose
->gtkaspell
->check_while_typing
)
11074 gtkaspell_highlight_all(compose
->gtkaspell
);
11078 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11080 Compose
*compose
= (Compose
*)data
;
11081 gint prev_autowrap
;
11082 GtkTextBuffer
*buffer
;
11084 if (compose
->focused_editable
11085 #ifndef GENERIC_UMPC
11086 && gtk_widget_has_focus(compose
->focused_editable
)
11089 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11090 GDK_SELECTION_CLIPBOARD
, NULL
);
11095 #ifndef GENERIC_UMPC
11096 gtk_widget_has_focus(compose
->text
) &&
11098 compose
->gtkaspell
&&
11099 compose
->gtkaspell
->check_while_typing
)
11100 gtkaspell_highlight_all(compose
->gtkaspell
);
11104 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11106 Compose
*compose
= (Compose
*)data
;
11107 if (compose
->focused_editable
11108 #ifndef GENERIC_UMPC
11109 && gtk_widget_has_focus(compose
->focused_editable
)
11112 entry_allsel(compose
->focused_editable
);
11115 static void textview_move_beginning_of_line (GtkTextView
*text
)
11117 GtkTextBuffer
*buffer
;
11121 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11123 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11124 mark
= gtk_text_buffer_get_insert(buffer
);
11125 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11126 gtk_text_iter_set_line_offset(&ins
, 0);
11127 gtk_text_buffer_place_cursor(buffer
, &ins
);
11130 static void textview_move_forward_character (GtkTextView
*text
)
11132 GtkTextBuffer
*buffer
;
11136 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11138 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11139 mark
= gtk_text_buffer_get_insert(buffer
);
11140 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11141 if (gtk_text_iter_forward_cursor_position(&ins
))
11142 gtk_text_buffer_place_cursor(buffer
, &ins
);
11145 static void textview_move_backward_character (GtkTextView
*text
)
11147 GtkTextBuffer
*buffer
;
11151 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11153 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11154 mark
= gtk_text_buffer_get_insert(buffer
);
11155 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11156 if (gtk_text_iter_backward_cursor_position(&ins
))
11157 gtk_text_buffer_place_cursor(buffer
, &ins
);
11160 static void textview_move_forward_word (GtkTextView
*text
)
11162 GtkTextBuffer
*buffer
;
11167 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11169 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11170 mark
= gtk_text_buffer_get_insert(buffer
);
11171 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11172 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11173 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11174 gtk_text_iter_backward_word_start(&ins
);
11175 gtk_text_buffer_place_cursor(buffer
, &ins
);
11179 static void textview_move_backward_word (GtkTextView
*text
)
11181 GtkTextBuffer
*buffer
;
11185 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11187 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11188 mark
= gtk_text_buffer_get_insert(buffer
);
11189 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11190 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11191 gtk_text_buffer_place_cursor(buffer
, &ins
);
11194 static void textview_move_end_of_line (GtkTextView
*text
)
11196 GtkTextBuffer
*buffer
;
11200 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11202 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11203 mark
= gtk_text_buffer_get_insert(buffer
);
11204 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11205 if (gtk_text_iter_forward_to_line_end(&ins
))
11206 gtk_text_buffer_place_cursor(buffer
, &ins
);
11209 static void textview_move_next_line (GtkTextView
*text
)
11211 GtkTextBuffer
*buffer
;
11216 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11218 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11219 mark
= gtk_text_buffer_get_insert(buffer
);
11220 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11221 offset
= gtk_text_iter_get_line_offset(&ins
);
11222 if (gtk_text_iter_forward_line(&ins
)) {
11223 gtk_text_iter_set_line_offset(&ins
, offset
);
11224 gtk_text_buffer_place_cursor(buffer
, &ins
);
11228 static void textview_move_previous_line (GtkTextView
*text
)
11230 GtkTextBuffer
*buffer
;
11235 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11237 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11238 mark
= gtk_text_buffer_get_insert(buffer
);
11239 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11240 offset
= gtk_text_iter_get_line_offset(&ins
);
11241 if (gtk_text_iter_backward_line(&ins
)) {
11242 gtk_text_iter_set_line_offset(&ins
, offset
);
11243 gtk_text_buffer_place_cursor(buffer
, &ins
);
11247 static void textview_delete_forward_character (GtkTextView
*text
)
11249 GtkTextBuffer
*buffer
;
11251 GtkTextIter ins
, end_iter
;
11253 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11255 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11256 mark
= gtk_text_buffer_get_insert(buffer
);
11257 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11259 if (gtk_text_iter_forward_char(&end_iter
)) {
11260 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11264 static void textview_delete_backward_character (GtkTextView
*text
)
11266 GtkTextBuffer
*buffer
;
11268 GtkTextIter ins
, end_iter
;
11270 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11272 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11273 mark
= gtk_text_buffer_get_insert(buffer
);
11274 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11276 if (gtk_text_iter_backward_char(&end_iter
)) {
11277 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11281 static void textview_delete_forward_word (GtkTextView
*text
)
11283 GtkTextBuffer
*buffer
;
11285 GtkTextIter ins
, end_iter
;
11287 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11289 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11290 mark
= gtk_text_buffer_get_insert(buffer
);
11291 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11293 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11294 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11298 static void textview_delete_backward_word (GtkTextView
*text
)
11300 GtkTextBuffer
*buffer
;
11302 GtkTextIter ins
, end_iter
;
11304 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11306 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11307 mark
= gtk_text_buffer_get_insert(buffer
);
11308 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11310 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11311 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11315 static void textview_delete_line (GtkTextView
*text
)
11317 GtkTextBuffer
*buffer
;
11319 GtkTextIter ins
, start_iter
, end_iter
;
11321 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11323 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11324 mark
= gtk_text_buffer_get_insert(buffer
);
11325 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11328 gtk_text_iter_set_line_offset(&start_iter
, 0);
11331 if (gtk_text_iter_ends_line(&end_iter
)){
11332 if (!gtk_text_iter_forward_char(&end_iter
))
11333 gtk_text_iter_backward_char(&start_iter
);
11336 gtk_text_iter_forward_to_line_end(&end_iter
);
11337 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11340 static void textview_delete_to_line_end (GtkTextView
*text
)
11342 GtkTextBuffer
*buffer
;
11344 GtkTextIter ins
, end_iter
;
11346 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11348 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11349 mark
= gtk_text_buffer_get_insert(buffer
);
11350 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11352 if (gtk_text_iter_ends_line(&end_iter
))
11353 gtk_text_iter_forward_char(&end_iter
);
11355 gtk_text_iter_forward_to_line_end(&end_iter
);
11356 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11359 #define DO_ACTION(name, act) { \
11360 if(!strcmp(name, a_name)) { \
11364 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11366 const gchar
*a_name
= gtk_action_get_name(action
);
11367 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11368 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11369 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11370 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11371 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11372 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11373 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11374 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11375 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11376 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11377 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11378 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11379 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11380 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11381 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11384 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11386 Compose
*compose
= (Compose
*)data
;
11387 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11388 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11390 action
= compose_call_advanced_action_from_path(gaction
);
11393 void (*do_action
) (GtkTextView
*text
);
11394 } action_table
[] = {
11395 {textview_move_beginning_of_line
},
11396 {textview_move_forward_character
},
11397 {textview_move_backward_character
},
11398 {textview_move_forward_word
},
11399 {textview_move_backward_word
},
11400 {textview_move_end_of_line
},
11401 {textview_move_next_line
},
11402 {textview_move_previous_line
},
11403 {textview_delete_forward_character
},
11404 {textview_delete_backward_character
},
11405 {textview_delete_forward_word
},
11406 {textview_delete_backward_word
},
11407 {textview_delete_line
},
11408 {textview_delete_to_line_end
}
11411 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11413 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11414 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11415 if (action_table
[action
].do_action
)
11416 action_table
[action
].do_action(text
);
11418 g_warning("not implemented yet");
11422 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11424 GtkAllocation allocation
;
11428 if (GTK_IS_EDITABLE(widget
)) {
11429 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11430 gtk_editable_set_position(GTK_EDITABLE(widget
),
11433 if ((parent
= gtk_widget_get_parent(widget
))
11434 && (parent
= gtk_widget_get_parent(parent
))
11435 && (parent
= gtk_widget_get_parent(parent
))) {
11436 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11437 gtk_widget_get_allocation(widget
, &allocation
);
11438 gint y
= allocation
.y
;
11439 gint height
= allocation
.height
;
11440 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11441 (GTK_SCROLLED_WINDOW(parent
));
11443 gfloat value
= gtk_adjustment_get_value(shown
);
11444 gfloat upper
= gtk_adjustment_get_upper(shown
);
11445 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11446 if (y
< (int)value
) {
11447 gtk_adjustment_set_value(shown
, y
- 1);
11449 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11450 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11451 gtk_adjustment_set_value(shown
,
11452 y
+ height
- (int)page_size
- 1);
11454 gtk_adjustment_set_value(shown
,
11455 (int)upper
- (int)page_size
- 1);
11462 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11463 compose
->focused_editable
= widget
;
11465 #ifdef GENERIC_UMPC
11466 if (GTK_IS_TEXT_VIEW(widget
)
11467 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11468 g_object_ref(compose
->notebook
);
11469 g_object_ref(compose
->edit_vbox
);
11470 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11471 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11472 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11473 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11474 g_object_unref(compose
->notebook
);
11475 g_object_unref(compose
->edit_vbox
);
11476 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11477 G_CALLBACK(compose_grab_focus_cb
),
11479 gtk_widget_grab_focus(widget
);
11480 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11481 G_CALLBACK(compose_grab_focus_cb
),
11483 } else if (!GTK_IS_TEXT_VIEW(widget
)
11484 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11485 g_object_ref(compose
->notebook
);
11486 g_object_ref(compose
->edit_vbox
);
11487 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11488 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11489 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11490 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11491 g_object_unref(compose
->notebook
);
11492 g_object_unref(compose
->edit_vbox
);
11493 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11494 G_CALLBACK(compose_grab_focus_cb
),
11496 gtk_widget_grab_focus(widget
);
11497 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11498 G_CALLBACK(compose_grab_focus_cb
),
11504 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11506 compose
->modified
= TRUE
;
11507 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11508 #ifndef GENERIC_UMPC
11509 compose_set_title(compose
);
11513 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11515 Compose
*compose
= (Compose
*)data
;
11516 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11519 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11521 Compose
*compose
= (Compose
*)data
;
11522 compose_wrap_all_full(compose
, TRUE
);
11525 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11527 Compose
*compose
= (Compose
*)data
;
11529 message_search_compose(compose
);
11532 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11535 Compose
*compose
= (Compose
*)data
;
11536 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11537 if (compose
->autowrap
)
11538 compose_wrap_all_full(compose
, TRUE
);
11539 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11542 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11545 Compose
*compose
= (Compose
*)data
;
11546 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11549 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11551 Compose
*compose
= (Compose
*)data
;
11553 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11554 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11557 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11559 Compose
*compose
= (Compose
*)data
;
11561 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11562 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11565 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11567 g_free(compose
->privacy_system
);
11568 g_free(compose
->encdata
);
11570 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11571 compose_update_privacy_system_menu_item(compose
, warn
);
11574 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11576 if (folder_item
!= NULL
) {
11577 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11578 privacy_system_can_sign(compose
->privacy_system
)) {
11579 compose_use_signing(compose
,
11580 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11582 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11583 privacy_system_can_encrypt(compose
->privacy_system
)) {
11584 compose_use_encryption(compose
,
11585 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11590 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11592 Compose
*compose
= (Compose
*)data
;
11594 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11595 gtk_widget_show(compose
->ruler_hbox
);
11596 prefs_common
.show_ruler
= TRUE
;
11598 gtk_widget_hide(compose
->ruler_hbox
);
11599 gtk_widget_queue_resize(compose
->edit_vbox
);
11600 prefs_common
.show_ruler
= FALSE
;
11604 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11605 GdkDragContext
*context
,
11608 GtkSelectionData
*data
,
11611 gpointer user_data
)
11613 Compose
*compose
= (Compose
*)user_data
;
11617 type
= gtk_selection_data_get_data_type(data
);
11618 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11619 && gtk_drag_get_source_widget(context
) !=
11620 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11621 list
= uri_list_extract_filenames(
11622 (const gchar
*)gtk_selection_data_get_data(data
));
11623 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11624 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11625 compose_attach_append
11626 (compose
, (const gchar
*)tmp
->data
,
11627 utf8_filename
, NULL
, NULL
);
11628 g_free(utf8_filename
);
11631 compose_changed_cb(NULL
, compose
);
11632 list_free_strings_full(list
);
11633 } else if (gtk_drag_get_source_widget(context
)
11634 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11635 /* comes from our summaryview */
11636 SummaryView
* summaryview
= NULL
;
11637 GSList
* list
= NULL
, *cur
= NULL
;
11639 if (mainwindow_get_mainwindow())
11640 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11643 list
= summary_get_selected_msg_list(summaryview
);
11645 for (cur
= list
; cur
; cur
= cur
->next
) {
11646 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11647 gchar
*file
= NULL
;
11649 file
= procmsg_get_message_file_full(msginfo
,
11652 compose_attach_append(compose
, (const gchar
*)file
,
11653 (const gchar
*)file
, "message/rfc822", NULL
);
11657 g_slist_free(list
);
11661 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11662 GdkDragContext
*drag_context
,
11664 guint time
, gpointer user_data
)
11666 /* not handling this signal makes compose_insert_drag_received_cb
11671 static gboolean completion_set_focus_to_subject
11672 (GtkWidget
*widget
,
11673 GdkEventKey
*event
,
11676 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11678 /* make backtab move to subject field */
11679 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11680 gtk_widget_grab_focus(compose
->subject_entry
);
11686 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11687 GdkDragContext
*drag_context
,
11690 GtkSelectionData
*data
,
11693 gpointer user_data
)
11695 Compose
*compose
= (Compose
*)user_data
;
11701 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11703 type
= gtk_selection_data_get_data_type(data
);
11704 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11705 AlertValue val
= G_ALERTDEFAULT
;
11706 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11708 list
= uri_list_extract_filenames(ddata
);
11709 num_files
= g_list_length(list
);
11710 if (list
== NULL
&& strstr(ddata
, "://")) {
11711 /* Assume a list of no files, and data has ://, is a remote link */
11712 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11713 gchar
*tmpfile
= get_tmp_file();
11714 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11716 compose_insert_file(compose
, tmpfile
);
11717 claws_unlink(tmpfile
);
11719 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11720 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11723 switch (prefs_common
.compose_dnd_mode
) {
11724 case COMPOSE_DND_ASK
:
11725 msg
= g_strdup_printf(
11727 "Do you want to insert the contents of the file "
11728 "into the message body, or attach it to the email?",
11729 "Do you want to insert the contents of the %d files "
11730 "into the message body, or attach them to the email?",
11733 val
= alertpanel_full(_("Insert or attach?"), msg
,
11734 NULL
, _("_Cancel"), NULL
, _("_Insert"), NULL
, _("_Attach"),
11735 ALERTFOCUS_SECOND
, TRUE
, NULL
, ALERT_QUESTION
);
11738 case COMPOSE_DND_INSERT
:
11739 val
= G_ALERTALTERNATE
;
11741 case COMPOSE_DND_ATTACH
:
11742 val
= G_ALERTOTHER
;
11745 /* unexpected case */
11746 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11749 if (val
& G_ALERTDISABLE
) {
11750 val
&= ~G_ALERTDISABLE
;
11751 /* remember what action to perform by default, only if we don't click Cancel */
11752 if (val
== G_ALERTALTERNATE
)
11753 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11754 else if (val
== G_ALERTOTHER
)
11755 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11758 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11759 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11760 list_free_strings_full(list
);
11762 } else if (val
== G_ALERTOTHER
) {
11763 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11764 list_free_strings_full(list
);
11768 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11769 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11771 list_free_strings_full(list
);
11772 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11777 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11778 GdkDragContext
*drag_context
,
11781 GtkSelectionData
*data
,
11784 gpointer user_data
)
11786 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11787 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11789 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11792 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11793 gchar
*decoded
=g_new(gchar
, strlen(email
));
11796 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11797 gtk_editable_delete_text(entry
, 0, -1);
11798 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11799 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11803 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11806 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11808 Compose
*compose
= (Compose
*)data
;
11810 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11811 compose
->return_receipt
= TRUE
;
11813 compose
->return_receipt
= FALSE
;
11816 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11818 Compose
*compose
= (Compose
*)data
;
11820 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11821 compose
->remove_references
= TRUE
;
11823 compose
->remove_references
= FALSE
;
11826 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11827 ComposeHeaderEntry
*headerentry
)
11829 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11830 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11831 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11835 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11836 GdkEventKey
*event
,
11837 ComposeHeaderEntry
*headerentry
)
11839 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11840 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11841 !(event
->state
& GDK_MODIFIER_MASK
) &&
11842 (event
->keyval
== GDK_KEY_BackSpace
) &&
11843 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11844 gtk_container_remove
11845 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11846 headerentry
->combo
);
11847 gtk_container_remove
11848 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11849 headerentry
->entry
);
11850 headerentry
->compose
->header_list
=
11851 g_slist_remove(headerentry
->compose
->header_list
,
11853 g_free(headerentry
);
11854 } else if (event
->keyval
== GDK_KEY_Tab
) {
11855 if (headerentry
->compose
->header_last
== headerentry
) {
11856 /* Override default next focus, and give it to subject_entry
11857 * instead of notebook tabs
11859 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11860 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11867 static gboolean
scroll_postpone(gpointer data
)
11869 Compose
*compose
= (Compose
*)data
;
11871 if (compose
->batch
)
11874 GTK_EVENTS_FLUSH();
11875 compose_show_first_last_header(compose
, FALSE
);
11879 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11880 ComposeHeaderEntry
*headerentry
)
11882 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11883 compose_create_header_entry(headerentry
->compose
);
11884 g_signal_handlers_disconnect_matched
11885 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11886 0, 0, NULL
, NULL
, headerentry
);
11888 if (!headerentry
->compose
->batch
)
11889 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11893 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11895 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11896 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11900 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11902 GtkAdjustment
*vadj
;
11904 cm_return_if_fail(compose
);
11909 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11910 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11911 vadj
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(gtk_widget_get_parent(compose
->header_table
)));
11912 gtk_adjustment_set_value(vadj
, (show_first
?
11913 gtk_adjustment_get_lower(vadj
) :
11914 (gtk_adjustment_get_upper(vadj
) -
11915 gtk_adjustment_get_page_size(vadj
))));
11918 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11919 const gchar
*text
, gint len
, Compose
*compose
)
11921 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11922 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11925 cm_return_if_fail(text
!= NULL
);
11927 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11928 G_CALLBACK(text_inserted
),
11930 if (paste_as_quotation
) {
11932 const gchar
*qmark
;
11934 GtkTextIter start_iter
;
11937 len
= strlen(text
);
11939 new_text
= g_strndup(text
, len
);
11941 qmark
= compose_quote_char_from_context(compose
);
11943 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11944 gtk_text_buffer_place_cursor(buffer
, iter
);
11946 pos
= gtk_text_iter_get_offset(iter
);
11948 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11949 _("Quote format error at line %d."));
11950 quote_fmt_reset_vartable();
11952 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11953 GINT_TO_POINTER(paste_as_quotation
- 1));
11955 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11956 gtk_text_buffer_place_cursor(buffer
, iter
);
11957 gtk_text_buffer_delete_mark(buffer
, mark
);
11959 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11960 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11961 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11962 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11963 gtk_text_buffer_delete_mark(buffer
, mark
);
11965 if (strcmp(text
, "\n") || compose
->automatic_break
11966 || gtk_text_iter_starts_line(iter
)) {
11967 GtkTextIter before_ins
;
11968 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11969 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11970 before_ins
= *iter
;
11971 gtk_text_iter_backward_chars(&before_ins
, len
);
11972 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11975 /* check if the preceding is just whitespace or quote */
11976 GtkTextIter start_line
;
11977 gchar
*tmp
= NULL
, *quote
= NULL
;
11978 gint quote_len
= 0, is_normal
= 0;
11979 start_line
= *iter
;
11980 gtk_text_iter_set_line_offset(&start_line
, 0);
11981 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11984 if (*tmp
== '\0') {
11987 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11995 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11997 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11998 iter
, text
, len
, "no_join", NULL
);
12003 if (!paste_as_quotation
) {
12004 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12005 compose_beautify_paragraph(compose
, iter
, FALSE
);
12006 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12007 gtk_text_buffer_delete_mark(buffer
, mark
);
12010 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12011 G_CALLBACK(text_inserted
),
12013 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12015 if (compose_can_autosave(compose
) &&
12016 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12017 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12018 compose
->draft_timeout_tag
= g_timeout_add
12019 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12023 static void compose_check_all(GtkAction
*action
, gpointer data
)
12025 Compose
*compose
= (Compose
*)data
;
12026 if (!compose
->gtkaspell
)
12029 if (gtk_widget_has_focus(compose
->subject_entry
))
12030 claws_spell_entry_check_all(
12031 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12033 gtkaspell_check_all(compose
->gtkaspell
);
12036 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12038 Compose
*compose
= (Compose
*)data
;
12039 if (compose
->gtkaspell
) {
12040 claws_spell_entry_recheck_all(
12041 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12042 gtkaspell_highlight_all(compose
->gtkaspell
);
12046 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12048 Compose
*compose
= (Compose
*)data
;
12049 if (!compose
->gtkaspell
) {
12050 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12054 if (gtk_widget_has_focus(compose
->subject_entry
))
12055 claws_spell_entry_check_backwards(
12056 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12058 gtkaspell_check_backwards(compose
->gtkaspell
);
12061 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12063 Compose
*compose
= (Compose
*)data
;
12064 if (!compose
->gtkaspell
) {
12065 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12069 if (gtk_widget_has_focus(compose
->subject_entry
))
12070 claws_spell_entry_check_forwards_go(
12071 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12073 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12078 *\brief Guess originating forward account from MsgInfo and several
12079 * "common preference" settings. Return NULL if no guess.
12081 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12083 PrefsAccount
*account
= NULL
;
12085 cm_return_val_if_fail(msginfo
, NULL
);
12086 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12087 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12089 if (msginfo
->folder
->prefs
->enable_default_account
)
12090 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12092 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12094 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12095 extract_address(to
);
12096 account
= account_find_from_address(to
, FALSE
);
12099 if (!account
&& prefs_common
.forward_account_autosel
) {
12101 if (!procheader_get_header_from_msginfo
12102 (msginfo
, &cc
, "Cc:")) {
12103 gchar
*buf
= cc
+ strlen("Cc:");
12104 extract_address(buf
);
12105 account
= account_find_from_address(buf
, FALSE
);
12110 if (!account
&& prefs_common
.forward_account_autosel
) {
12111 gchar
*deliveredto
= NULL
;
12112 if (!procheader_get_header_from_msginfo
12113 (msginfo
, &deliveredto
, "Delivered-To:")) {
12114 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12115 extract_address(buf
);
12116 account
= account_find_from_address(buf
, FALSE
);
12117 g_free(deliveredto
);
12122 account
= msginfo
->folder
->folder
->account
;
12127 gboolean
compose_close(Compose
*compose
)
12131 cm_return_val_if_fail(compose
, FALSE
);
12133 if (!g_mutex_trylock(&compose
->mutex
)) {
12134 /* we have to wait for the (possibly deferred by auto-save)
12135 * drafting to be done, before destroying the compose under
12137 debug_print("waiting for drafting to finish...\n");
12138 compose_allow_user_actions(compose
, FALSE
);
12139 if (compose
->close_timeout_tag
== 0) {
12140 compose
->close_timeout_tag
=
12141 g_timeout_add (500, (GSourceFunc
) compose_close
,
12147 if (compose
->draft_timeout_tag
>= 0) {
12148 g_source_remove(compose
->draft_timeout_tag
);
12149 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12152 gtk_window_get_position(GTK_WINDOW(compose
->window
), &x
, &y
);
12153 if (!compose
->batch
) {
12154 prefs_common
.compose_x
= x
;
12155 prefs_common
.compose_y
= y
;
12157 g_mutex_unlock(&compose
->mutex
);
12158 compose_destroy(compose
);
12163 * Add entry field for each address in list.
12164 * \param compose E-Mail composition object.
12165 * \param listAddress List of (formatted) E-Mail addresses.
12167 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12170 node
= listAddress
;
12172 addr
= ( gchar
* ) node
->data
;
12173 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12174 node
= g_list_next( node
);
12178 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12179 guint action
, gboolean opening_multiple
)
12181 gchar
*body
= NULL
;
12182 GSList
*new_msglist
= NULL
;
12183 MsgInfo
*tmp_msginfo
= NULL
;
12184 gboolean originally_enc
= FALSE
;
12185 gboolean originally_sig
= FALSE
;
12186 Compose
*compose
= NULL
;
12187 gchar
*s_system
= NULL
;
12189 cm_return_if_fail(msginfo_list
!= NULL
);
12191 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12192 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12193 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12195 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12196 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12197 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12198 orig_msginfo
, mimeinfo
);
12199 if (tmp_msginfo
!= NULL
) {
12200 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12202 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12203 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12204 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12206 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12207 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12208 if (orig_msginfo
->tags
) {
12209 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12210 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12216 if (!opening_multiple
&& msgview
!= NULL
)
12217 body
= messageview_get_selection(msgview
);
12220 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12221 procmsg_msginfo_free(&tmp_msginfo
);
12222 g_slist_free(new_msglist
);
12224 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12226 if (compose
&& originally_enc
) {
12227 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12230 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12231 compose_force_signing(compose
, compose
->account
, s_system
);
12235 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12238 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12241 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12242 && msginfo_list
!= NULL
12243 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12244 GSList
*cur
= msginfo_list
;
12245 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12246 "messages. Opening the windows "
12247 "could take some time. Do you "
12248 "want to continue?"),
12249 g_slist_length(msginfo_list
));
12250 if (g_slist_length(msginfo_list
) > 9
12251 && alertpanel(_("Warning"), msg
, NULL
, _("_Cancel"), NULL
, _("_Yes"),
12252 NULL
, NULL
, ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12257 /* We'll open multiple compose windows */
12258 /* let the WM place the next windows */
12259 compose_force_window_origin
= FALSE
;
12260 for (; cur
; cur
= cur
->next
) {
12262 tmplist
.data
= cur
->data
;
12263 tmplist
.next
= NULL
;
12264 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12266 compose_force_window_origin
= TRUE
;
12268 /* forwarding multiple mails as attachments is done via a
12269 * single compose window */
12270 if (msginfo_list
!= NULL
) {
12271 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12272 } else if (msgview
!= NULL
) {
12274 tmplist
.data
= msgview
->msginfo
;
12275 tmplist
.next
= NULL
;
12276 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12278 debug_print("Nothing to reply to\n");
12283 void compose_check_for_email_account(Compose
*compose
)
12285 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12291 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12292 ac
= account_get_cur_account();
12293 if (ac
->protocol
== A_NNTP
) {
12294 list
= account_get_list();
12296 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12297 curr
= (PrefsAccount
*) list
->data
;
12298 if (curr
->protocol
!= A_NNTP
) {
12304 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12309 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12310 const gchar
*address
)
12312 GSList
*msginfo_list
= NULL
;
12313 gchar
*body
= messageview_get_selection(msgview
);
12316 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12318 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12319 compose_check_for_email_account(compose
);
12320 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12321 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12322 compose_reply_set_subject(compose
, msginfo
);
12325 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12328 void compose_set_position(Compose
*compose
, gint pos
)
12330 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12332 gtkut_text_view_set_position(text
, pos
);
12335 gboolean
compose_search_string(Compose
*compose
,
12336 const gchar
*str
, gboolean case_sens
)
12338 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12340 return gtkut_text_view_search_string(text
, str
, case_sens
);
12343 gboolean
compose_search_string_backward(Compose
*compose
,
12344 const gchar
*str
, gboolean case_sens
)
12346 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12348 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12351 /* allocate a msginfo structure and populate its data from a compose data structure */
12352 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12354 MsgInfo
*newmsginfo
;
12356 gchar date
[RFC822_DATE_BUFFSIZE
];
12358 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12360 newmsginfo
= procmsg_msginfo_new();
12363 get_rfc822_date(date
, sizeof(date
));
12364 newmsginfo
->date
= g_strdup(date
);
12367 if (compose
->from_name
) {
12368 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12369 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12373 if (compose
->subject_entry
)
12374 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12376 /* to, cc, reply-to, newsgroups */
12377 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12378 gchar
*header
= gtk_editable_get_chars(
12380 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12381 gchar
*entry
= gtk_editable_get_chars(
12382 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12384 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12385 if ( newmsginfo
->to
== NULL
) {
12386 newmsginfo
->to
= g_strdup(entry
);
12387 } else if (entry
&& *entry
) {
12388 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12389 g_free(newmsginfo
->to
);
12390 newmsginfo
->to
= tmp
;
12393 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12394 if ( newmsginfo
->cc
== NULL
) {
12395 newmsginfo
->cc
= g_strdup(entry
);
12396 } else if (entry
&& *entry
) {
12397 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12398 g_free(newmsginfo
->cc
);
12399 newmsginfo
->cc
= tmp
;
12402 if ( strcasecmp(header
,
12403 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12404 if ( newmsginfo
->newsgroups
== NULL
) {
12405 newmsginfo
->newsgroups
= g_strdup(entry
);
12406 } else if (entry
&& *entry
) {
12407 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12408 g_free(newmsginfo
->newsgroups
);
12409 newmsginfo
->newsgroups
= tmp
;
12417 /* other data is unset */
12423 /* update compose's dictionaries from folder dict settings */
12424 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12425 FolderItem
*folder_item
)
12427 cm_return_if_fail(compose
!= NULL
);
12429 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12430 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12432 if (prefs
->enable_default_dictionary
)
12433 gtkaspell_change_dict(compose
->gtkaspell
,
12434 prefs
->default_dictionary
, FALSE
);
12435 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12436 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12437 prefs
->default_alt_dictionary
);
12438 if (prefs
->enable_default_dictionary
12439 || prefs
->enable_default_alt_dictionary
)
12440 compose_spell_menu_changed(compose
);
12445 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12447 Compose
*compose
= (Compose
*)data
;
12449 cm_return_if_fail(compose
!= NULL
);
12451 gtk_widget_grab_focus(compose
->text
);
12454 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12456 gtk_combo_box_popup(GTK_COMBO_BOX(data
));