2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2021 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>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
57 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
64 #include "mainwindow.h"
66 #ifndef USE_ALT_ADDRBOOK
67 #include "addressbook.h"
69 #include "addressbook-dbus.h"
70 #include "addressadd.h"
72 #include "folderview.h"
75 #include "stock_pixmap.h"
76 #include "send_message.h"
79 #include "customheader.h"
80 #include "prefs_common.h"
81 #include "prefs_account.h"
85 #include "procheader.h"
87 #include "statusbar.h"
89 #include "quoted-printable.h"
93 #include "gtkshruler.h"
95 #include "alertpanel.h"
96 #include "manage_window.h"
98 #include "folder_item_prefs.h"
99 #include "addr_compl.h"
100 #include "quote_fmt.h"
102 #include "foldersel.h"
105 #include "message_search.h"
106 #include "combobox.h"
110 #include "autofaces.h"
111 #include "spell_entry.h"
113 #include "file-utils.h"
116 #include "password.h"
117 #include "ldapserver.h"
131 #define N_ATTACH_COLS (N_COL_COLUMNS)
135 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
= -1,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
= 0,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
141 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
145 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
147 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
148 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
149 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
150 } ComposeCallAdvancedAction
;
154 PRIORITY_HIGHEST
= 1,
163 COMPOSE_INSERT_SUCCESS
,
164 COMPOSE_INSERT_READ_ERROR
,
165 COMPOSE_INSERT_INVALID_CHARACTER
,
166 COMPOSE_INSERT_NO_FILE
167 } ComposeInsertResult
;
171 COMPOSE_WRITE_FOR_SEND
,
172 COMPOSE_WRITE_FOR_STORE
177 COMPOSE_QUOTE_FORCED
,
184 SUBJECT_FIELD_PRESENT
,
189 #define B64_LINE_SIZE 57
190 #define B64_BUFFSIZE 77
192 #define MAX_REFERENCES_LEN 999
194 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
195 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
197 #define COMPOSE_PRIVACY_WARNING() { \
198 alertpanel_error(_("You have opted to sign and/or encrypt this " \
199 "message but have not selected a privacy system.\n\n" \
200 "Signing and encrypting have been disabled for this " \
204 static GdkColor default_header_bgcolor
= {
211 static GdkColor 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 GdkColor quote_color1
=
821 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
822 static GdkColor quote_color2
=
823 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
824 static GdkColor quote_color3
=
825 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
827 static GdkColor quote_bgcolor1
=
828 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
829 static GdkColor quote_bgcolor2
=
830 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
831 static GdkColor quote_bgcolor3
=
832 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
834 static GdkColor signature_color
= {
841 static GdkColor uri_color
= {
848 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
850 GtkTextBuffer
*buffer
;
851 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
853 buffer
= gtk_text_view_get_buffer(text
);
855 if (prefs_common
.enable_color
) {
856 /* grab the quote colors, converting from an int to a GdkColor */
857 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1
],
859 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2
],
861 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3
],
863 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1_BG
],
865 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2_BG
],
867 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3_BG
],
869 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_SIGNATURE
],
871 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_URI
],
874 signature_color
= quote_color1
= quote_color2
= quote_color3
=
875 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
878 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
879 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
880 "foreground-gdk", "e_color1
,
881 "paragraph-background-gdk", "e_bgcolor1
,
883 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
884 "foreground-gdk", "e_color2
,
885 "paragraph-background-gdk", "e_bgcolor2
,
887 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
888 "foreground-gdk", "e_color3
,
889 "paragraph-background-gdk", "e_bgcolor3
,
892 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
893 "foreground-gdk", "e_color1
,
895 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
896 "foreground-gdk", "e_color2
,
898 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
899 "foreground-gdk", "e_color3
,
903 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
904 "foreground-gdk", &signature_color
,
907 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
908 "foreground-gdk", &uri_color
,
910 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
911 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
914 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
917 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
920 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
922 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
925 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
927 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
930 #define SCROLL_TO_CURSOR(compose) { \
931 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
932 gtk_text_view_get_buffer( \
933 GTK_TEXT_VIEW(compose->text))); \
934 gtk_text_view_scroll_mark_onscreen( \
935 GTK_TEXT_VIEW(compose->text), \
939 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
942 if (folderidentifier
) {
943 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
944 prefs_common
.compose_save_to_history
= add_history(
945 prefs_common
.compose_save_to_history
, folderidentifier
);
946 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
947 prefs_common
.compose_save_to_history
);
950 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
951 if (folderidentifier
)
952 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
954 gtk_entry_set_text(GTK_ENTRY(entry
), "");
957 static gchar
*compose_get_save_to(Compose
*compose
)
960 gchar
*result
= NULL
;
961 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
962 result
= gtk_editable_get_chars(entry
, 0, -1);
965 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
966 prefs_common
.compose_save_to_history
= add_history(
967 prefs_common
.compose_save_to_history
, result
);
968 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
969 prefs_common
.compose_save_to_history
);
974 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
975 GList
*attach_files
, GList
*listAddress
)
978 GtkTextView
*textview
;
979 GtkTextBuffer
*textbuf
;
981 const gchar
*subject_format
= NULL
;
982 const gchar
*body_format
= NULL
;
983 gchar
*mailto_from
= NULL
;
984 PrefsAccount
*mailto_account
= NULL
;
985 MsgInfo
* dummyinfo
= NULL
;
986 gint cursor_pos
= -1;
987 MailField mfield
= NO_FIELD_PRESENT
;
991 /* check if mailto defines a from */
992 if (mailto
&& *mailto
!= '\0') {
993 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
994 /* mailto defines a from, check if we can get account prefs from it,
995 if not, the account prefs will be guessed using other ways, but we'll keep
998 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
999 if (mailto_account
== NULL
) {
1001 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
1002 extract_address(tmp_from
);
1003 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
1007 account
= mailto_account
;
1010 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1011 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
1012 account
= account_find_from_id(item
->prefs
->default_account
);
1014 /* if no account prefs set, fallback to the current one */
1015 if (!account
) account
= cur_account
;
1016 cm_return_val_if_fail(account
!= NULL
, NULL
);
1018 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1019 compose_apply_folder_privacy_settings(compose
, item
);
1021 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1022 (account
->default_encrypt
|| account
->default_sign
))
1023 COMPOSE_PRIVACY_WARNING();
1025 /* override from name if mailto asked for it */
1027 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1028 g_free(mailto_from
);
1030 /* override from name according to folder properties */
1031 if (item
&& item
->prefs
&&
1032 item
->prefs
->compose_with_format
&&
1033 item
->prefs
->compose_override_from_format
&&
1034 *item
->prefs
->compose_override_from_format
!= '\0') {
1039 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1041 /* decode \-escape sequences in the internal representation of the quote format */
1042 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1043 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1046 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1047 compose
->gtkaspell
);
1049 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1051 quote_fmt_scan_string(tmp
);
1054 buf
= quote_fmt_get_buffer();
1056 alertpanel_error(_("New message From format error."));
1058 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1059 quote_fmt_reset_vartable();
1060 quote_fmtlex_destroy();
1065 compose
->replyinfo
= NULL
;
1066 compose
->fwdinfo
= NULL
;
1068 textview
= GTK_TEXT_VIEW(compose
->text
);
1069 textbuf
= gtk_text_view_get_buffer(textview
);
1070 compose_create_tags(textview
, compose
);
1072 undo_block(compose
->undostruct
);
1074 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1077 if (account
->auto_sig
)
1078 compose_insert_sig(compose
, FALSE
);
1079 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1080 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1082 if (account
->protocol
!= A_NNTP
) {
1083 if (mailto
&& *mailto
!= '\0') {
1084 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1087 compose_set_folder_prefs(compose
, item
, TRUE
);
1089 if (item
&& item
->ret_rcpt
) {
1090 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1093 if (mailto
&& *mailto
!= '\0') {
1094 if (!strchr(mailto
, '@'))
1095 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1097 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1098 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1099 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1100 mfield
= TO_FIELD_PRESENT
;
1103 * CLAWS: just don't allow return receipt request, even if the user
1104 * may want to send an email. simple but foolproof.
1106 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1108 compose_add_field_list( compose
, listAddress
);
1110 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1111 subject_format
= item
->prefs
->compose_subject_format
;
1112 body_format
= item
->prefs
->compose_body_format
;
1113 } else if (account
->compose_with_format
) {
1114 subject_format
= account
->compose_subject_format
;
1115 body_format
= account
->compose_body_format
;
1116 } else if (prefs_common
.compose_with_format
) {
1117 subject_format
= prefs_common
.compose_subject_format
;
1118 body_format
= prefs_common
.compose_body_format
;
1121 if (subject_format
|| body_format
) {
1124 && *subject_format
!= '\0' )
1126 gchar
*subject
= NULL
;
1131 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1133 /* decode \-escape sequences in the internal representation of the quote format */
1134 tmp
= g_malloc(strlen(subject_format
)+1);
1135 pref_get_unescaped_pref(tmp
, subject_format
);
1137 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1139 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1140 compose
->gtkaspell
);
1142 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1144 quote_fmt_scan_string(tmp
);
1147 buf
= quote_fmt_get_buffer();
1149 alertpanel_error(_("New message subject format error."));
1151 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1152 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1153 quote_fmt_reset_vartable();
1154 quote_fmtlex_destroy();
1158 mfield
= SUBJECT_FIELD_PRESENT
;
1162 && *body_format
!= '\0' )
1165 GtkTextBuffer
*buffer
;
1166 GtkTextIter start
, end
;
1170 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1172 text
= GTK_TEXT_VIEW(compose
->text
);
1173 buffer
= gtk_text_view_get_buffer(text
);
1174 gtk_text_buffer_get_start_iter(buffer
, &start
);
1175 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1176 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1178 compose_quote_fmt(compose
, dummyinfo
,
1180 NULL
, tmp
, FALSE
, TRUE
,
1181 _("The body of the \"New message\" template has an error at line %d."));
1182 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1183 quote_fmt_reset_vartable();
1187 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1188 gtkaspell_highlight_all(compose
->gtkaspell
);
1190 mfield
= BODY_FIELD_PRESENT
;
1194 procmsg_msginfo_free( &dummyinfo
);
1200 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1201 ainfo
= (AttachInfo
*) curr
->data
;
1203 compose_insert_file(compose
, ainfo
->file
);
1205 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1206 ainfo
->content_type
, ainfo
->charset
);
1210 compose_show_first_last_header(compose
, TRUE
);
1212 /* Set save folder */
1213 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1214 gchar
*folderidentifier
;
1216 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1217 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1218 folderidentifier
= folder_item_get_identifier(item
);
1219 compose_set_save_to(compose
, folderidentifier
);
1220 g_free(folderidentifier
);
1223 /* Place cursor according to provided input (mfield) */
1225 case NO_FIELD_PRESENT
:
1226 if (compose
->header_last
)
1227 gtk_widget_grab_focus(compose
->header_last
->entry
);
1229 case TO_FIELD_PRESENT
:
1230 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1232 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1235 gtk_widget_grab_focus(compose
->subject_entry
);
1237 case SUBJECT_FIELD_PRESENT
:
1238 textview
= GTK_TEXT_VIEW(compose
->text
);
1241 textbuf
= gtk_text_view_get_buffer(textview
);
1244 mark
= gtk_text_buffer_get_insert(textbuf
);
1245 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1246 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1248 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1249 * only defers where it comes to the variable body
1250 * is not null. If no body is present compose->text
1251 * will be null in which case you cannot place the
1252 * cursor inside the component so. An empty component
1253 * is therefore created before placing the cursor
1255 case BODY_FIELD_PRESENT
:
1256 cursor_pos
= quote_fmt_get_cursor_pos();
1257 if (cursor_pos
== -1)
1258 gtk_widget_grab_focus(compose
->header_last
->entry
);
1260 gtk_widget_grab_focus(compose
->text
);
1264 undo_unblock(compose
->undostruct
);
1266 if (prefs_common
.auto_exteditor
)
1267 compose_exec_ext_editor(compose
);
1269 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1271 SCROLL_TO_CURSOR(compose
);
1273 compose
->modified
= FALSE
;
1274 compose_set_title(compose
);
1276 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1281 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1282 gboolean override_pref
, const gchar
*system
)
1284 const gchar
*privacy
= NULL
;
1286 cm_return_if_fail(compose
!= NULL
);
1287 cm_return_if_fail(account
!= NULL
);
1289 if (privacy_system_can_encrypt(compose
->privacy_system
) == FALSE
||
1290 (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
))
1293 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1294 privacy
= account
->default_privacy_system
;
1298 GSList
*privacy_avail
= privacy_get_system_ids();
1299 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1300 privacy
= (gchar
*)(privacy_avail
->data
);
1302 g_slist_free_full(privacy_avail
, g_free
);
1304 if (privacy
!= NULL
) {
1306 g_free(compose
->privacy_system
);
1307 compose
->privacy_system
= NULL
;
1308 g_free(compose
->encdata
);
1309 compose
->encdata
= NULL
;
1311 if (compose
->privacy_system
== NULL
)
1312 compose
->privacy_system
= g_strdup(privacy
);
1313 else if (*(compose
->privacy_system
) == '\0') {
1314 g_free(compose
->privacy_system
);
1315 g_free(compose
->encdata
);
1316 compose
->encdata
= NULL
;
1317 compose
->privacy_system
= g_strdup(privacy
);
1319 compose_update_privacy_system_menu_item(compose
, FALSE
);
1320 compose_use_encryption(compose
, TRUE
);
1324 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1326 const gchar
*privacy
= NULL
;
1327 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
)
1330 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1331 privacy
= account
->default_privacy_system
;
1335 GSList
*privacy_avail
= privacy_get_system_ids();
1336 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1337 privacy
= (gchar
*)(privacy_avail
->data
);
1341 if (privacy
!= NULL
) {
1343 g_free(compose
->privacy_system
);
1344 compose
->privacy_system
= NULL
;
1345 g_free(compose
->encdata
);
1346 compose
->encdata
= NULL
;
1348 if (compose
->privacy_system
== NULL
)
1349 compose
->privacy_system
= g_strdup(privacy
);
1350 compose_update_privacy_system_menu_item(compose
, FALSE
);
1351 compose_use_signing(compose
, TRUE
);
1355 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1359 Compose
*compose
= NULL
;
1361 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1363 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1364 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1366 list_len
= g_slist_length(msginfo_list
);
1370 case COMPOSE_REPLY_TO_ADDRESS
:
1371 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1372 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1374 case COMPOSE_REPLY_WITH_QUOTE
:
1375 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1376 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1378 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1379 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1380 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1382 case COMPOSE_REPLY_TO_SENDER
:
1383 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1384 FALSE
, FALSE
, TRUE
, body
);
1386 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1387 compose
= compose_followup_and_reply_to(msginfo
,
1388 COMPOSE_QUOTE_CHECK
,
1389 FALSE
, FALSE
, body
);
1391 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1392 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1393 FALSE
, FALSE
, TRUE
, body
);
1395 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1396 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1397 FALSE
, FALSE
, TRUE
, NULL
);
1399 case COMPOSE_REPLY_TO_ALL
:
1400 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1401 TRUE
, FALSE
, FALSE
, body
);
1403 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1404 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1405 TRUE
, FALSE
, FALSE
, body
);
1407 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1408 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1409 TRUE
, FALSE
, FALSE
, NULL
);
1411 case COMPOSE_REPLY_TO_LIST
:
1412 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1413 FALSE
, TRUE
, FALSE
, body
);
1415 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1416 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1417 FALSE
, TRUE
, FALSE
, body
);
1419 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1420 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1421 FALSE
, TRUE
, FALSE
, NULL
);
1423 case COMPOSE_FORWARD
:
1424 if (prefs_common
.forward_as_attachment
) {
1425 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1428 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1432 case COMPOSE_FORWARD_INLINE
:
1433 /* check if we reply to more than one Message */
1434 if (list_len
== 1) {
1435 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1438 /* more messages FALL THROUGH */
1439 case COMPOSE_FORWARD_AS_ATTACH
:
1440 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1442 case COMPOSE_REDIRECT
:
1443 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1446 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1449 if (compose
== NULL
) {
1450 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1454 compose
->rmode
= mode
;
1455 switch (compose
->rmode
) {
1457 case COMPOSE_REPLY_WITH_QUOTE
:
1458 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1459 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1460 debug_print("reply mode Normal\n");
1461 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1462 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1464 case COMPOSE_REPLY_TO_SENDER
:
1465 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1466 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1467 debug_print("reply mode Sender\n");
1468 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1470 case COMPOSE_REPLY_TO_ALL
:
1471 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1472 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1473 debug_print("reply mode All\n");
1474 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1476 case COMPOSE_REPLY_TO_LIST
:
1477 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1478 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1479 debug_print("reply mode List\n");
1480 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1482 case COMPOSE_REPLY_TO_ADDRESS
:
1483 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1491 static Compose
*compose_reply(MsgInfo
*msginfo
,
1492 ComposeQuoteMode quote_mode
,
1498 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1499 to_sender
, FALSE
, body
);
1502 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1503 ComposeQuoteMode quote_mode
,
1508 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1509 to_sender
, TRUE
, body
);
1512 static void compose_extract_original_charset(Compose
*compose
)
1514 MsgInfo
*info
= NULL
;
1515 if (compose
->replyinfo
) {
1516 info
= compose
->replyinfo
;
1517 } else if (compose
->fwdinfo
) {
1518 info
= compose
->fwdinfo
;
1519 } else if (compose
->targetinfo
) {
1520 info
= compose
->targetinfo
;
1523 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1524 MimeInfo
*partinfo
= mimeinfo
;
1525 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1526 partinfo
= procmime_mimeinfo_next(partinfo
);
1528 compose
->orig_charset
=
1529 g_strdup(procmime_mimeinfo_get_parameter(
1530 partinfo
, "charset"));
1532 procmime_mimeinfo_free_all(&mimeinfo
);
1536 #define SIGNAL_BLOCK(buffer) { \
1537 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1538 G_CALLBACK(compose_changed_cb), \
1540 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1541 G_CALLBACK(text_inserted), \
1545 #define SIGNAL_UNBLOCK(buffer) { \
1546 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1547 G_CALLBACK(compose_changed_cb), \
1549 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1550 G_CALLBACK(text_inserted), \
1554 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1555 ComposeQuoteMode quote_mode
,
1556 gboolean to_all
, gboolean to_ml
,
1558 gboolean followup_and_reply_to
,
1562 PrefsAccount
*account
= NULL
;
1563 GtkTextView
*textview
;
1564 GtkTextBuffer
*textbuf
;
1565 gboolean quote
= FALSE
;
1566 const gchar
*qmark
= NULL
;
1567 const gchar
*body_fmt
= NULL
;
1568 gchar
*s_system
= NULL
;
1570 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1571 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1573 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1575 cm_return_val_if_fail(account
!= NULL
, NULL
);
1577 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1578 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1580 compose
->updating
= TRUE
;
1582 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1583 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1585 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1586 if (!compose
->replyinfo
)
1587 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1589 compose_extract_original_charset(compose
);
1591 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1592 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1594 /* Set save folder */
1595 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1596 gchar
*folderidentifier
;
1598 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1599 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1600 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1601 compose_set_save_to(compose
, folderidentifier
);
1602 g_free(folderidentifier
);
1605 if (compose_parse_header(compose
, msginfo
) < 0) {
1606 compose
->updating
= FALSE
;
1607 compose_destroy(compose
);
1611 /* override from name according to folder properties */
1612 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1613 msginfo
->folder
->prefs
->reply_with_format
&&
1614 msginfo
->folder
->prefs
->reply_override_from_format
&&
1615 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1620 /* decode \-escape sequences in the internal representation of the quote format */
1621 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1622 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1625 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1626 compose
->gtkaspell
);
1628 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1630 quote_fmt_scan_string(tmp
);
1633 buf
= quote_fmt_get_buffer();
1635 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1637 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1638 quote_fmt_reset_vartable();
1639 quote_fmtlex_destroy();
1644 textview
= (GTK_TEXT_VIEW(compose
->text
));
1645 textbuf
= gtk_text_view_get_buffer(textview
);
1646 compose_create_tags(textview
, compose
);
1648 undo_block(compose
->undostruct
);
1650 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1651 gtkaspell_block_check(compose
->gtkaspell
);
1654 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1655 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1656 /* use the reply format of folder (if enabled), or the account's one
1657 (if enabled) or fallback to the global reply format, which is always
1658 enabled (even if empty), and use the relevant quotemark */
1660 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1661 msginfo
->folder
->prefs
->reply_with_format
) {
1662 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1663 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1665 } else if (account
->reply_with_format
) {
1666 qmark
= account
->reply_quotemark
;
1667 body_fmt
= account
->reply_body_format
;
1670 qmark
= prefs_common
.quotemark
;
1671 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1672 body_fmt
= gettext(prefs_common
.quotefmt
);
1679 /* empty quotemark is not allowed */
1680 if (qmark
== NULL
|| *qmark
== '\0')
1682 compose_quote_fmt(compose
, compose
->replyinfo
,
1683 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1684 _("The body of the \"Reply\" template has an error at line %d."));
1685 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1686 quote_fmt_reset_vartable();
1689 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1690 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1693 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1694 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1695 compose_force_signing(compose
, account
, s_system
);
1699 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1700 ((account
->default_encrypt
|| account
->default_sign
) ||
1701 (account
->default_encrypt_reply
&& MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) ||
1702 (account
->default_sign_reply
&& MSG_IS_SIGNED(compose
->replyinfo
->flags
))))
1703 COMPOSE_PRIVACY_WARNING();
1705 SIGNAL_BLOCK(textbuf
);
1707 if (account
->auto_sig
)
1708 compose_insert_sig(compose
, FALSE
);
1710 compose_wrap_all(compose
);
1713 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1714 gtkaspell_highlight_all(compose
->gtkaspell
);
1715 gtkaspell_unblock_check(compose
->gtkaspell
);
1717 SIGNAL_UNBLOCK(textbuf
);
1719 gtk_widget_grab_focus(compose
->text
);
1721 undo_unblock(compose
->undostruct
);
1723 if (prefs_common
.auto_exteditor
)
1724 compose_exec_ext_editor(compose
);
1726 compose
->modified
= FALSE
;
1727 compose_set_title(compose
);
1729 compose
->updating
= FALSE
;
1730 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1731 SCROLL_TO_CURSOR(compose
);
1733 if (compose
->deferred_destroy
) {
1734 compose_destroy(compose
);
1742 #define INSERT_FW_HEADER(var, hdr) \
1743 if (msginfo->var && *msginfo->var) { \
1744 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1745 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1746 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1749 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1750 gboolean as_attach
, const gchar
*body
,
1751 gboolean no_extedit
,
1755 GtkTextView
*textview
;
1756 GtkTextBuffer
*textbuf
;
1757 gint cursor_pos
= -1;
1760 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1761 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1763 if (!account
&& !(account
= compose_find_account(msginfo
)))
1764 account
= cur_account
;
1766 if (!prefs_common
.forward_as_attachment
)
1767 mode
= COMPOSE_FORWARD_INLINE
;
1769 mode
= COMPOSE_FORWARD
;
1770 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1771 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1773 compose
->updating
= TRUE
;
1774 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1775 if (!compose
->fwdinfo
)
1776 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1778 compose_extract_original_charset(compose
);
1780 if (msginfo
->subject
&& *msginfo
->subject
) {
1781 gchar
*buf
, *buf2
, *p
;
1783 buf
= p
= g_strdup(msginfo
->subject
);
1784 p
+= subject_get_prefix_length(p
);
1785 memmove(buf
, p
, strlen(p
) + 1);
1787 buf2
= g_strdup_printf("Fw: %s", buf
);
1788 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1794 /* override from name according to folder properties */
1795 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1796 msginfo
->folder
->prefs
->forward_with_format
&&
1797 msginfo
->folder
->prefs
->forward_override_from_format
&&
1798 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1802 MsgInfo
*full_msginfo
= NULL
;
1805 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1807 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1809 /* decode \-escape sequences in the internal representation of the quote format */
1810 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1811 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1814 gtkaspell_block_check(compose
->gtkaspell
);
1815 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1816 compose
->gtkaspell
);
1818 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1820 quote_fmt_scan_string(tmp
);
1823 buf
= quote_fmt_get_buffer();
1825 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1827 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1828 quote_fmt_reset_vartable();
1829 quote_fmtlex_destroy();
1832 procmsg_msginfo_free(&full_msginfo
);
1835 textview
= GTK_TEXT_VIEW(compose
->text
);
1836 textbuf
= gtk_text_view_get_buffer(textview
);
1837 compose_create_tags(textview
, compose
);
1839 undo_block(compose
->undostruct
);
1843 msgfile
= procmsg_get_message_file(msginfo
);
1844 if (!is_file_exist(msgfile
))
1845 g_warning("%s: file does not exist", msgfile
);
1847 compose_attach_append(compose
, msgfile
, msgfile
,
1848 "message/rfc822", NULL
);
1852 const gchar
*qmark
= NULL
;
1853 const gchar
*body_fmt
= NULL
;
1854 MsgInfo
*full_msginfo
;
1856 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1858 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1860 /* use the forward format of folder (if enabled), or the account's one
1861 (if enabled) or fallback to the global forward format, which is always
1862 enabled (even if empty), and use the relevant quotemark */
1863 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1864 msginfo
->folder
->prefs
->forward_with_format
) {
1865 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1866 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1868 } else if (account
->forward_with_format
) {
1869 qmark
= account
->forward_quotemark
;
1870 body_fmt
= account
->forward_body_format
;
1873 qmark
= prefs_common
.fw_quotemark
;
1874 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1875 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1880 /* empty quotemark is not allowed */
1881 if (qmark
== NULL
|| *qmark
== '\0')
1884 compose_quote_fmt(compose
, full_msginfo
,
1885 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1886 _("The body of the \"Forward\" template has an error at line %d."));
1887 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1888 quote_fmt_reset_vartable();
1889 compose_attach_parts(compose
, msginfo
);
1891 procmsg_msginfo_free(&full_msginfo
);
1894 SIGNAL_BLOCK(textbuf
);
1896 if (account
->auto_sig
)
1897 compose_insert_sig(compose
, FALSE
);
1899 compose_wrap_all(compose
);
1902 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1903 gtkaspell_highlight_all(compose
->gtkaspell
);
1904 gtkaspell_unblock_check(compose
->gtkaspell
);
1906 SIGNAL_UNBLOCK(textbuf
);
1908 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1909 (account
->default_encrypt
|| account
->default_sign
))
1910 COMPOSE_PRIVACY_WARNING();
1912 cursor_pos
= quote_fmt_get_cursor_pos();
1913 if (cursor_pos
== -1)
1914 gtk_widget_grab_focus(compose
->header_last
->entry
);
1916 gtk_widget_grab_focus(compose
->text
);
1918 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1919 compose_exec_ext_editor(compose
);
1922 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1923 gchar
*folderidentifier
;
1925 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1926 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1927 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1928 compose_set_save_to(compose
, folderidentifier
);
1929 g_free(folderidentifier
);
1932 undo_unblock(compose
->undostruct
);
1934 compose
->modified
= FALSE
;
1935 compose_set_title(compose
);
1937 compose
->updating
= FALSE
;
1938 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1939 SCROLL_TO_CURSOR(compose
);
1941 if (compose
->deferred_destroy
) {
1942 compose_destroy(compose
);
1946 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1951 #undef INSERT_FW_HEADER
1953 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1956 GtkTextView
*textview
;
1957 GtkTextBuffer
*textbuf
;
1961 gboolean single_mail
= TRUE
;
1963 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1965 if (g_slist_length(msginfo_list
) > 1)
1966 single_mail
= FALSE
;
1968 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1969 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1972 /* guess account from first selected message */
1974 !(account
= compose_find_account(msginfo_list
->data
)))
1975 account
= cur_account
;
1977 cm_return_val_if_fail(account
!= NULL
, NULL
);
1979 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1980 if (msginfo
->data
) {
1981 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1982 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1986 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1987 g_warning("no msginfo_list");
1991 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1992 compose_apply_folder_privacy_settings(compose
, ((MsgInfo
*)msginfo_list
->data
)->folder
);
1993 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1994 (account
->default_encrypt
|| account
->default_sign
))
1995 COMPOSE_PRIVACY_WARNING();
1997 compose
->updating
= TRUE
;
1999 /* override from name according to folder properties */
2000 if (msginfo_list
->data
) {
2001 MsgInfo
*msginfo
= msginfo_list
->data
;
2003 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
2004 msginfo
->folder
->prefs
->forward_with_format
&&
2005 msginfo
->folder
->prefs
->forward_override_from_format
&&
2006 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
2011 /* decode \-escape sequences in the internal representation of the quote format */
2012 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
2013 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2016 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2017 compose
->gtkaspell
);
2019 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2021 quote_fmt_scan_string(tmp
);
2024 buf
= quote_fmt_get_buffer();
2026 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2028 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2029 quote_fmt_reset_vartable();
2030 quote_fmtlex_destroy();
2036 textview
= GTK_TEXT_VIEW(compose
->text
);
2037 textbuf
= gtk_text_view_get_buffer(textview
);
2038 compose_create_tags(textview
, compose
);
2040 undo_block(compose
->undostruct
);
2041 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2042 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2044 if (!is_file_exist(msgfile
))
2045 g_warning("%s: file does not exist", msgfile
);
2047 compose_attach_append(compose
, msgfile
, msgfile
,
2048 "message/rfc822", NULL
);
2053 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2054 if (info
->subject
&& *info
->subject
) {
2055 gchar
*buf
, *buf2
, *p
;
2057 buf
= p
= g_strdup(info
->subject
);
2058 p
+= subject_get_prefix_length(p
);
2059 memmove(buf
, p
, strlen(p
) + 1);
2061 buf2
= g_strdup_printf("Fw: %s", buf
);
2062 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2068 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2069 _("Fw: multiple emails"));
2072 SIGNAL_BLOCK(textbuf
);
2074 if (account
->auto_sig
)
2075 compose_insert_sig(compose
, FALSE
);
2077 compose_wrap_all(compose
);
2079 SIGNAL_UNBLOCK(textbuf
);
2081 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2082 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2084 if (prefs_common
.auto_exteditor
)
2085 compose_exec_ext_editor(compose
);
2087 gtk_widget_grab_focus(compose
->header_last
->entry
);
2088 undo_unblock(compose
->undostruct
);
2089 compose
->modified
= FALSE
;
2090 compose_set_title(compose
);
2092 compose
->updating
= FALSE
;
2093 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2094 SCROLL_TO_CURSOR(compose
);
2096 if (compose
->deferred_destroy
) {
2097 compose_destroy(compose
);
2101 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2106 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2108 GtkTextIter start
= *iter
;
2109 GtkTextIter end_iter
;
2110 int start_pos
= gtk_text_iter_get_offset(&start
);
2112 if (!compose
->account
->sig_sep
)
2115 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2116 start_pos
+strlen(compose
->account
->sig_sep
));
2118 /* check sig separator */
2119 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2120 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2122 /* check end of line (\n) */
2123 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2124 start_pos
+strlen(compose
->account
->sig_sep
));
2125 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2126 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2127 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2128 if (!strcmp(tmp
,"\n")) {
2140 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2142 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2143 Compose
*compose
= (Compose
*)data
;
2144 FolderItem
*old_item
= NULL
;
2145 FolderItem
*new_item
= NULL
;
2146 gchar
*old_id
, *new_id
;
2148 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2149 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2152 old_item
= hookdata
->item
;
2153 new_item
= hookdata
->item2
;
2155 old_id
= folder_item_get_identifier(old_item
);
2156 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2158 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2159 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2160 compose
->targetinfo
->folder
= new_item
;
2163 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2164 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2165 compose
->replyinfo
->folder
= new_item
;
2168 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2169 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2170 compose
->fwdinfo
->folder
= new_item
;
2178 static void compose_colorize_signature(Compose
*compose
)
2180 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2182 GtkTextIter end_iter
;
2183 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2184 while (gtk_text_iter_forward_line(&iter
))
2185 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2186 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2187 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2191 #define BLOCK_WRAP() { \
2192 prev_autowrap = compose->autowrap; \
2193 buffer = gtk_text_view_get_buffer( \
2194 GTK_TEXT_VIEW(compose->text)); \
2195 compose->autowrap = FALSE; \
2197 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2198 G_CALLBACK(compose_changed_cb), \
2200 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2201 G_CALLBACK(text_inserted), \
2204 #define UNBLOCK_WRAP() { \
2205 compose->autowrap = prev_autowrap; \
2206 if (compose->autowrap) { \
2207 gint old = compose->draft_timeout_tag; \
2208 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2209 compose_wrap_all(compose); \
2210 compose->draft_timeout_tag = old; \
2213 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2214 G_CALLBACK(compose_changed_cb), \
2216 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2217 G_CALLBACK(text_inserted), \
2221 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2223 Compose
*compose
= NULL
;
2224 PrefsAccount
*account
= NULL
;
2225 GtkTextView
*textview
;
2226 GtkTextBuffer
*textbuf
;
2230 gboolean use_signing
= FALSE
;
2231 gboolean use_encryption
= FALSE
;
2232 gchar
*privacy_system
= NULL
;
2233 int priority
= PRIORITY_NORMAL
;
2234 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2235 gboolean autowrap
= prefs_common
.autowrap
;
2236 gboolean autoindent
= prefs_common
.auto_indent
;
2237 HeaderEntry
*manual_headers
= NULL
;
2239 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2240 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2242 if (compose_put_existing_to_front(msginfo
)) {
2246 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2247 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2248 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2249 gchar
*queueheader_buf
= NULL
;
2252 /* Select Account from queue headers */
2253 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2254 "X-Claws-Account-Id:")) {
2255 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2256 account
= account_find_from_id(id
);
2257 g_free(queueheader_buf
);
2259 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2260 "X-Sylpheed-Account-Id:")) {
2261 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2262 account
= account_find_from_id(id
);
2263 g_free(queueheader_buf
);
2265 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2267 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2268 account
= account_find_from_id(id
);
2269 g_free(queueheader_buf
);
2271 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2273 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2274 account
= account_find_from_id(id
);
2275 g_free(queueheader_buf
);
2277 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2279 account
= account_find_from_address(queueheader_buf
, FALSE
);
2280 g_free(queueheader_buf
);
2282 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2284 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2285 use_signing
= param
;
2286 g_free(queueheader_buf
);
2288 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2289 "X-Sylpheed-Sign:")) {
2290 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2291 use_signing
= param
;
2292 g_free(queueheader_buf
);
2294 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2295 "X-Claws-Encrypt:")) {
2296 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2297 use_encryption
= param
;
2298 g_free(queueheader_buf
);
2300 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2301 "X-Sylpheed-Encrypt:")) {
2302 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2303 use_encryption
= param
;
2304 g_free(queueheader_buf
);
2306 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2307 "X-Claws-Auto-Wrapping:")) {
2308 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2310 g_free(queueheader_buf
);
2312 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2313 "X-Claws-Auto-Indent:")) {
2314 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2316 g_free(queueheader_buf
);
2318 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2319 "X-Claws-Privacy-System:")) {
2320 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2321 g_free(queueheader_buf
);
2323 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2324 "X-Sylpheed-Privacy-System:")) {
2325 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2326 g_free(queueheader_buf
);
2328 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2330 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2332 g_free(queueheader_buf
);
2334 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2336 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2337 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2338 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2339 if (orig_item
!= NULL
) {
2340 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2344 g_free(queueheader_buf
);
2346 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2348 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2349 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2350 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2351 if (orig_item
!= NULL
) {
2352 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2356 g_free(queueheader_buf
);
2358 /* Get manual headers */
2359 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2360 "X-Claws-Manual-Headers:")) {
2361 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2362 if (listmh
&& *listmh
!= '\0') {
2363 debug_print("Got manual headers: %s\n", listmh
);
2364 manual_headers
= procheader_entries_from_str(listmh
);
2367 g_free(queueheader_buf
);
2370 account
= msginfo
->folder
->folder
->account
;
2373 if (!account
&& prefs_common
.reedit_account_autosel
) {
2375 if (!procheader_get_header_from_msginfo(msginfo
, &from
, "FROM:")) {
2376 extract_address(from
);
2377 account
= account_find_from_address(from
, FALSE
);
2382 account
= cur_account
;
2384 cm_return_val_if_fail(account
!= NULL
, NULL
);
2386 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2388 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
2389 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
2390 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2391 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2392 compose
->autowrap
= autowrap
;
2393 compose
->replyinfo
= replyinfo
;
2394 compose
->fwdinfo
= fwdinfo
;
2396 compose
->updating
= TRUE
;
2397 compose
->priority
= priority
;
2399 if (privacy_system
!= NULL
) {
2400 compose
->privacy_system
= privacy_system
;
2401 compose_use_signing(compose
, use_signing
);
2402 compose_use_encryption(compose
, use_encryption
);
2403 compose_update_privacy_system_menu_item(compose
, FALSE
);
2405 compose_activate_privacy_system(compose
, account
, FALSE
);
2407 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2408 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
2409 (account
->default_encrypt
|| account
->default_sign
))
2410 COMPOSE_PRIVACY_WARNING();
2412 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2413 compose
->targetinfo
->tags
= g_slist_copy(msginfo
->tags
);
2415 compose_extract_original_charset(compose
);
2417 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2418 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2419 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2420 gchar
*queueheader_buf
= NULL
;
2422 /* Set message save folder */
2423 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "SCF:")) {
2424 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2425 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2426 compose_set_save_to(compose
, &queueheader_buf
[4]);
2427 g_free(queueheader_buf
);
2429 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "RRCPT:")) {
2430 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2432 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2434 g_free(queueheader_buf
);
2438 if (compose_parse_header(compose
, msginfo
) < 0) {
2439 compose
->updating
= FALSE
;
2440 compose_destroy(compose
);
2443 compose_reedit_set_entry(compose
, msginfo
);
2445 textview
= GTK_TEXT_VIEW(compose
->text
);
2446 textbuf
= gtk_text_view_get_buffer(textview
);
2447 compose_create_tags(textview
, compose
);
2449 mark
= gtk_text_buffer_get_insert(textbuf
);
2450 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2452 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2453 G_CALLBACK(compose_changed_cb
),
2456 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2457 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2459 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2462 fp
= procmime_get_first_text_content(msginfo
);
2465 g_warning("Can't get text part");
2469 gchar buf
[BUFFSIZE
];
2470 gboolean prev_autowrap
;
2471 GtkTextBuffer
*buffer
;
2473 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2475 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2481 compose_attach_parts(compose
, msginfo
);
2483 compose_colorize_signature(compose
);
2485 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2486 G_CALLBACK(compose_changed_cb
),
2489 if (manual_headers
!= NULL
) {
2490 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2491 procheader_entries_free(manual_headers
);
2492 compose
->updating
= FALSE
;
2493 compose_destroy(compose
);
2496 procheader_entries_free(manual_headers
);
2499 gtk_widget_grab_focus(compose
->text
);
2501 if (prefs_common
.auto_exteditor
) {
2502 compose_exec_ext_editor(compose
);
2504 compose
->modified
= FALSE
;
2505 compose_set_title(compose
);
2507 compose
->updating
= FALSE
;
2508 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2509 SCROLL_TO_CURSOR(compose
);
2511 if (compose
->deferred_destroy
) {
2512 compose_destroy(compose
);
2516 compose
->sig_str
= account_get_signature_str(compose
->account
);
2518 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2523 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2530 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2533 account
= account_get_reply_account(msginfo
,
2534 prefs_common
.reply_account_autosel
);
2535 cm_return_val_if_fail(account
!= NULL
, NULL
);
2537 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2539 compose
->updating
= TRUE
;
2541 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2542 compose
->replyinfo
= NULL
;
2543 compose
->fwdinfo
= NULL
;
2545 compose_show_first_last_header(compose
, TRUE
);
2547 gtk_widget_grab_focus(compose
->header_last
->entry
);
2549 filename
= procmsg_get_message_file(msginfo
);
2551 if (filename
== NULL
) {
2552 compose
->updating
= FALSE
;
2553 compose_destroy(compose
);
2558 compose
->redirect_filename
= filename
;
2560 /* Set save folder */
2561 item
= msginfo
->folder
;
2562 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2563 gchar
*folderidentifier
;
2565 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2566 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2567 folderidentifier
= folder_item_get_identifier(item
);
2568 compose_set_save_to(compose
, folderidentifier
);
2569 g_free(folderidentifier
);
2572 compose_attach_parts(compose
, msginfo
);
2574 if (msginfo
->subject
)
2575 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2577 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2579 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2580 _("The body of the \"Redirect\" template has an error at line %d."));
2581 quote_fmt_reset_vartable();
2582 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2584 compose_colorize_signature(compose
);
2587 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2588 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2589 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2592 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2593 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2595 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2596 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2597 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2598 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2599 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2601 if (compose
->toolbar
->draft_btn
)
2602 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2603 if (compose
->toolbar
->insert_btn
)
2604 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2605 if (compose
->toolbar
->attach_btn
)
2606 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2607 if (compose
->toolbar
->sig_btn
)
2608 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2609 if (compose
->toolbar
->exteditor_btn
)
2610 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2611 if (compose
->toolbar
->linewrap_current_btn
)
2612 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2613 if (compose
->toolbar
->linewrap_all_btn
)
2614 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2615 if (compose
->toolbar
->privacy_sign_btn
)
2616 gtk_widget_set_sensitive(compose
->toolbar
->privacy_sign_btn
, FALSE
);
2617 if (compose
->toolbar
->privacy_encrypt_btn
)
2618 gtk_widget_set_sensitive(compose
->toolbar
->privacy_encrypt_btn
, FALSE
);
2620 compose
->modified
= FALSE
;
2621 compose_set_title(compose
);
2622 compose
->updating
= FALSE
;
2623 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2624 SCROLL_TO_CURSOR(compose
);
2626 if (compose
->deferred_destroy
) {
2627 compose_destroy(compose
);
2631 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2636 const GList
*compose_get_compose_list(void)
2638 return compose_list
;
2641 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2642 ComposeEntryType type
, ComposePrefType pref_type
)
2644 const gchar
*header
;
2646 gboolean in_quote
= FALSE
;
2647 if (!address
|| *address
== '\0') return;
2654 header
= N_("Bcc:");
2656 case COMPOSE_REPLYTO
:
2657 header
= N_("Reply-To:");
2659 case COMPOSE_NEWSGROUPS
:
2660 header
= N_("Newsgroups:");
2662 case COMPOSE_FOLLOWUPTO
:
2663 header
= N_( "Followup-To:");
2665 case COMPOSE_INREPLYTO
:
2666 header
= N_( "In-Reply-To:");
2673 header
= prefs_common_translated_header_name(header
);
2675 cur
= begin
= (gchar
*)address
;
2677 /* we separate the line by commas, but not if we're inside a quoted
2679 while (*cur
!= '\0') {
2681 in_quote
= !in_quote
;
2682 if (*cur
== ',' && !in_quote
) {
2683 gchar
*tmp
= g_strdup(begin
);
2685 tmp
[cur
-begin
]='\0';
2688 while (*tmp
== ' ' || *tmp
== '\t')
2690 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2691 compose_entry_indicate(compose
, tmp
);
2698 gchar
*tmp
= g_strdup(begin
);
2700 tmp
[cur
-begin
]='\0';
2701 while (*tmp
== ' ' || *tmp
== '\t')
2703 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2704 compose_entry_indicate(compose
, tmp
);
2709 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2714 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2715 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2716 if (gtk_entry_get_text(entry
) &&
2717 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2718 gtk_widget_modify_base(
2719 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2720 GTK_STATE_NORMAL
, &default_header_bgcolor
);
2721 gtk_widget_modify_text(
2722 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2723 GTK_STATE_NORMAL
, &default_header_color
);
2728 void compose_toolbar_cb(gint action
, gpointer data
)
2730 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2731 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2733 cm_return_if_fail(compose
!= NULL
);
2737 compose_send_cb(NULL
, compose
);
2740 compose_send_later_cb(NULL
, compose
);
2743 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2746 compose_insert_file_cb(NULL
, compose
);
2749 compose_attach_cb(NULL
, compose
);
2752 compose_insert_sig(compose
, FALSE
);
2755 compose_insert_sig(compose
, TRUE
);
2758 compose_ext_editor_cb(NULL
, compose
);
2760 case A_LINEWRAP_CURRENT
:
2761 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2763 case A_LINEWRAP_ALL
:
2764 compose_wrap_all_full(compose
, TRUE
);
2767 compose_address_cb(NULL
, compose
);
2770 case A_CHECK_SPELLING
:
2771 compose_check_all(NULL
, compose
);
2774 case A_PRIVACY_SIGN
:
2776 case A_PRIVACY_ENCRYPT
:
2783 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2788 gchar
*subject
= NULL
;
2792 gchar
**attach
= NULL
;
2793 gchar
*inreplyto
= NULL
;
2794 MailField mfield
= NO_FIELD_PRESENT
;
2796 /* get mailto parts but skip from */
2797 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2800 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2801 mfield
= TO_FIELD_PRESENT
;
2804 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2806 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2808 if (!g_utf8_validate (subject
, -1, NULL
)) {
2809 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2810 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2813 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2815 mfield
= SUBJECT_FIELD_PRESENT
;
2818 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2819 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2822 gboolean prev_autowrap
= compose
->autowrap
;
2824 compose
->autowrap
= FALSE
;
2826 mark
= gtk_text_buffer_get_insert(buffer
);
2827 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2829 if (!g_utf8_validate (body
, -1, NULL
)) {
2830 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2831 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2834 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2836 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2838 compose
->autowrap
= prev_autowrap
;
2839 if (compose
->autowrap
)
2840 compose_wrap_all(compose
);
2841 mfield
= BODY_FIELD_PRESENT
;
2845 gint i
= 0, att
= 0;
2846 gchar
*warn_files
= NULL
;
2847 while (attach
[i
] != NULL
) {
2848 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2849 if (utf8_filename
) {
2850 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2851 gchar
*tmp
= g_strdup_printf("%s%s\n",
2852 warn_files
?warn_files
:"",
2858 g_free(utf8_filename
);
2860 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2865 alertpanel_notice(ngettext(
2866 "The following file has been attached: \n%s",
2867 "The following files have been attached: \n%s", att
), warn_files
);
2872 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2885 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2887 static HeaderEntry hentry
[] = {
2888 {"Reply-To:", NULL
, TRUE
},
2889 {"Cc:", NULL
, TRUE
},
2890 {"References:", NULL
, FALSE
},
2891 {"Bcc:", NULL
, TRUE
},
2892 {"Newsgroups:", NULL
, TRUE
},
2893 {"Followup-To:", NULL
, TRUE
},
2894 {"List-Post:", NULL
, FALSE
},
2895 {"X-Priority:", NULL
, FALSE
},
2896 {NULL
, NULL
, FALSE
}
2913 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2915 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2916 procheader_get_header_fields(fp
, hentry
);
2919 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2920 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2922 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2925 g_free(hentry
[H_REPLY_TO
].body
);
2926 hentry
[H_REPLY_TO
].body
= NULL
;
2928 if (hentry
[H_CC
].body
!= NULL
) {
2929 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2930 g_free(hentry
[H_CC
].body
);
2931 hentry
[H_CC
].body
= NULL
;
2933 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2934 if (compose
->mode
== COMPOSE_REEDIT
)
2935 compose
->references
= hentry
[H_REFERENCES
].body
;
2937 compose
->references
= compose_parse_references
2938 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2939 g_free(hentry
[H_REFERENCES
].body
);
2941 hentry
[H_REFERENCES
].body
= NULL
;
2943 if (hentry
[H_BCC
].body
!= NULL
) {
2944 if (compose
->mode
== COMPOSE_REEDIT
)
2946 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2947 g_free(hentry
[H_BCC
].body
);
2948 hentry
[H_BCC
].body
= NULL
;
2950 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2951 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2952 hentry
[H_NEWSGROUPS
].body
= NULL
;
2954 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2955 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2956 compose
->followup_to
=
2957 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2960 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2961 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2963 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2964 gchar
*to
= NULL
, *start
= NULL
;
2966 extract_address(hentry
[H_LIST_POST
].body
);
2967 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2968 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2970 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2971 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2974 g_free(compose
->ml_post
);
2975 compose
->ml_post
= to
;
2978 g_free(hentry
[H_LIST_POST
].body
);
2979 hentry
[H_LIST_POST
].body
= NULL
;
2982 /* CLAWS - X-Priority */
2983 if (compose
->mode
== COMPOSE_REEDIT
)
2984 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2987 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2988 g_free(hentry
[H_X_PRIORITY
].body
);
2990 hentry
[H_X_PRIORITY
].body
= NULL
;
2992 if (priority
< PRIORITY_HIGHEST
||
2993 priority
> PRIORITY_LOWEST
)
2994 priority
= PRIORITY_NORMAL
;
2996 compose
->priority
= priority
;
2999 if (compose
->mode
== COMPOSE_REEDIT
) {
3000 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3001 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
3003 if (msginfo
->msgid
&& *msginfo
->msgid
&&
3004 compose
->folder
!= NULL
&&
3005 compose
->folder
->stype
== F_DRAFT
)
3006 compose
->msgid
= g_strdup(msginfo
->msgid
);
3008 if (msginfo
->msgid
&& *msginfo
->msgid
)
3009 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
3011 if (!compose
->references
) {
3012 if (msginfo
->msgid
&& *msginfo
->msgid
) {
3013 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3014 compose
->references
=
3015 g_strdup_printf("<%s>\n\t<%s>",
3019 compose
->references
=
3020 g_strconcat("<", msginfo
->msgid
, ">",
3022 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3023 compose
->references
=
3024 g_strconcat("<", msginfo
->inreplyto
, ">",
3033 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3038 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3040 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3041 procheader_get_header_fields(fp
, entries
);
3045 while (he
!= NULL
&& he
->name
!= NULL
) {
3047 GtkListStore
*model
= NULL
;
3049 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3050 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3051 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3052 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3053 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3060 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3062 GSList
*ref_id_list
, *cur
;
3066 ref_id_list
= references_list_append(NULL
, ref
);
3067 if (!ref_id_list
) return NULL
;
3068 if (msgid
&& *msgid
)
3069 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3074 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3075 /* "<" + Message-ID + ">" + CR+LF+TAB */
3076 len
+= strlen((gchar
*)cur
->data
) + 5;
3078 if (len
> MAX_REFERENCES_LEN
) {
3079 /* remove second message-ID */
3080 if (ref_id_list
&& ref_id_list
->next
&&
3081 ref_id_list
->next
->next
) {
3082 g_free(ref_id_list
->next
->data
);
3083 ref_id_list
= g_slist_remove
3084 (ref_id_list
, ref_id_list
->next
->data
);
3086 slist_free_strings_full(ref_id_list
);
3093 new_ref
= g_string_new("");
3094 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3095 if (new_ref
->len
> 0)
3096 g_string_append(new_ref
, "\n\t");
3097 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3100 slist_free_strings_full(ref_id_list
);
3102 new_ref_str
= new_ref
->str
;
3103 g_string_free(new_ref
, FALSE
);
3108 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3109 const gchar
*fmt
, const gchar
*qmark
,
3110 const gchar
*body
, gboolean rewrap
,
3111 gboolean need_unescape
,
3112 const gchar
*err_msg
)
3114 MsgInfo
* dummyinfo
= NULL
;
3115 gchar
*quote_str
= NULL
;
3117 gboolean prev_autowrap
;
3118 const gchar
*trimmed_body
= body
;
3119 gint cursor_pos
= -1;
3120 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3121 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3126 SIGNAL_BLOCK(buffer
);
3129 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3130 msginfo
= dummyinfo
;
3133 if (qmark
!= NULL
) {
3135 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3136 compose
->gtkaspell
);
3138 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3140 quote_fmt_scan_string(qmark
);
3143 buf
= quote_fmt_get_buffer();
3146 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3148 Xstrdup_a(quote_str
, buf
, goto error
)
3151 if (fmt
&& *fmt
!= '\0') {
3154 while (*trimmed_body
== '\n')
3158 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3159 compose
->gtkaspell
);
3161 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3163 if (need_unescape
) {
3166 /* decode \-escape sequences in the internal representation of the quote format */
3167 tmp
= g_malloc(strlen(fmt
)+1);
3168 pref_get_unescaped_pref(tmp
, fmt
);
3169 quote_fmt_scan_string(tmp
);
3173 quote_fmt_scan_string(fmt
);
3177 buf
= quote_fmt_get_buffer();
3180 gint line
= quote_fmt_get_line();
3181 alertpanel_error(err_msg
, line
);
3189 prev_autowrap
= compose
->autowrap
;
3190 compose
->autowrap
= FALSE
;
3192 mark
= gtk_text_buffer_get_insert(buffer
);
3193 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3194 if (g_utf8_validate(buf
, -1, NULL
)) {
3195 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3197 gchar
*tmpout
= NULL
;
3198 tmpout
= conv_codeset_strdup
3199 (buf
, conv_get_locale_charset_str_no_utf8(),
3201 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3203 tmpout
= g_malloc(strlen(buf
)*2+1);
3204 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3206 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3210 cursor_pos
= quote_fmt_get_cursor_pos();
3211 if (cursor_pos
== -1)
3212 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3213 compose
->set_cursor_pos
= cursor_pos
;
3215 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3216 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3217 gtk_text_buffer_place_cursor(buffer
, &iter
);
3219 compose
->autowrap
= prev_autowrap
;
3220 if (compose
->autowrap
&& rewrap
)
3221 compose_wrap_all(compose
);
3228 SIGNAL_UNBLOCK(buffer
);
3230 procmsg_msginfo_free( &dummyinfo
);
3235 /* if ml_post is of type addr@host and from is of type
3236 * addr-anything@host, return TRUE
3238 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3240 gchar
*left_ml
= NULL
;
3241 gchar
*right_ml
= NULL
;
3242 gchar
*left_from
= NULL
;
3243 gchar
*right_from
= NULL
;
3244 gboolean result
= FALSE
;
3246 if (!ml_post
|| !from
)
3249 left_ml
= g_strdup(ml_post
);
3250 if (strstr(left_ml
, "@")) {
3251 right_ml
= strstr(left_ml
, "@")+1;
3252 *(strstr(left_ml
, "@")) = '\0';
3255 left_from
= g_strdup(from
);
3256 if (strstr(left_from
, "@")) {
3257 right_from
= strstr(left_from
, "@")+1;
3258 *(strstr(left_from
, "@")) = '\0';
3261 if (right_ml
&& right_from
3262 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3263 && !strcmp(right_from
, right_ml
)) {
3272 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3273 gboolean respect_default_to
)
3277 if (!folder
|| !folder
->prefs
)
3280 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3281 compose_entry_append(compose
, folder
->prefs
->default_to
,
3282 COMPOSE_TO
, PREF_FOLDER
);
3283 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3285 if (folder
->prefs
->enable_default_cc
) {
3286 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3287 COMPOSE_CC
, PREF_FOLDER
);
3288 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3290 if (folder
->prefs
->enable_default_bcc
) {
3291 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3292 COMPOSE_BCC
, PREF_FOLDER
);
3293 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3295 if (folder
->prefs
->enable_default_replyto
) {
3296 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3297 COMPOSE_REPLYTO
, PREF_FOLDER
);
3298 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3302 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3307 if (!compose
|| !msginfo
)
3310 if (msginfo
->subject
&& *msginfo
->subject
) {
3311 buf
= p
= g_strdup(msginfo
->subject
);
3312 p
+= subject_get_prefix_length(p
);
3313 memmove(buf
, p
, strlen(p
) + 1);
3315 buf2
= g_strdup_printf("Re: %s", buf
);
3316 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3321 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3324 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3325 gboolean to_all
, gboolean to_ml
,
3327 gboolean followup_and_reply_to
)
3329 GSList
*cc_list
= NULL
;
3332 gchar
*replyto
= NULL
;
3333 gchar
*ac_email
= NULL
;
3335 gboolean reply_to_ml
= FALSE
;
3336 gboolean default_reply_to
= FALSE
;
3338 cm_return_if_fail(compose
->account
!= NULL
);
3339 cm_return_if_fail(msginfo
!= NULL
);
3341 reply_to_ml
= to_ml
&& compose
->ml_post
;
3343 default_reply_to
= msginfo
->folder
&&
3344 msginfo
->folder
->prefs
->enable_default_reply_to
;
3346 if (compose
->account
->protocol
!= A_NNTP
) {
3347 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3349 if (reply_to_ml
&& !default_reply_to
) {
3351 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3354 /* normal answer to ml post with a reply-to */
3355 compose_entry_append(compose
,
3357 COMPOSE_TO
, PREF_ML
);
3358 if (compose
->replyto
)
3359 compose_entry_append(compose
,
3361 COMPOSE_CC
, PREF_ML
);
3363 /* answer to subscription confirmation */
3364 if (compose
->replyto
)
3365 compose_entry_append(compose
,
3367 COMPOSE_TO
, PREF_ML
);
3368 else if (msginfo
->from
)
3369 compose_entry_append(compose
,
3371 COMPOSE_TO
, PREF_ML
);
3374 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3375 compose_entry_append(compose
,
3376 msginfo
->folder
->prefs
->default_reply_to
,
3377 COMPOSE_TO
, PREF_FOLDER
);
3378 compose_entry_indicate(compose
,
3379 msginfo
->folder
->prefs
->default_reply_to
);
3385 compose_entry_append(compose
, msginfo
->from
,
3386 COMPOSE_TO
, PREF_NONE
);
3388 Xstrdup_a(tmp1
, msginfo
->from
, return);
3389 extract_address(tmp1
);
3390 compose_entry_append(compose
,
3391 (!account_find_from_address(tmp1
, FALSE
))
3394 COMPOSE_TO
, PREF_NONE
);
3395 if (compose
->replyto
)
3396 compose_entry_append(compose
,
3398 COMPOSE_CC
, PREF_NONE
);
3400 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3401 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3402 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3403 if (compose
->replyto
) {
3404 compose_entry_append(compose
,
3406 COMPOSE_TO
, PREF_NONE
);
3408 compose_entry_append(compose
,
3409 msginfo
->from
? msginfo
->from
: "",
3410 COMPOSE_TO
, PREF_NONE
);
3413 /* replying to own mail, use original recp */
3414 compose_entry_append(compose
,
3415 msginfo
->to
? msginfo
->to
: "",
3416 COMPOSE_TO
, PREF_NONE
);
3417 compose_entry_append(compose
,
3418 msginfo
->cc
? msginfo
->cc
: "",
3419 COMPOSE_CC
, PREF_NONE
);
3424 if (to_sender
|| (compose
->followup_to
&&
3425 !strncmp(compose
->followup_to
, "poster", 6)))
3426 compose_entry_append
3428 (compose
->replyto
? compose
->replyto
:
3429 msginfo
->from
? msginfo
->from
: ""),
3430 COMPOSE_TO
, PREF_NONE
);
3432 else if (followup_and_reply_to
|| to_all
) {
3433 compose_entry_append
3435 (compose
->replyto
? compose
->replyto
:
3436 msginfo
->from
? msginfo
->from
: ""),
3437 COMPOSE_TO
, PREF_NONE
);
3439 compose_entry_append
3441 compose
->followup_to
? compose
->followup_to
:
3442 compose
->newsgroups
? compose
->newsgroups
: "",
3443 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3445 compose_entry_append
3447 msginfo
->cc
? msginfo
->cc
: "",
3448 COMPOSE_CC
, PREF_NONE
);
3451 compose_entry_append
3453 compose
->followup_to
? compose
->followup_to
:
3454 compose
->newsgroups
? compose
->newsgroups
: "",
3455 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3457 compose_reply_set_subject(compose
, msginfo
);
3459 if (to_ml
&& compose
->ml_post
) return;
3460 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3462 if (compose
->replyto
) {
3463 Xstrdup_a(replyto
, compose
->replyto
, return);
3464 extract_address(replyto
);
3466 if (msginfo
->from
) {
3467 Xstrdup_a(from
, msginfo
->from
, return);
3468 extract_address(from
);
3471 if (replyto
&& from
)
3472 cc_list
= address_list_append_with_comments(cc_list
, from
);
3473 if (to_all
&& msginfo
->folder
&&
3474 msginfo
->folder
->prefs
->enable_default_reply_to
)
3475 cc_list
= address_list_append_with_comments(cc_list
,
3476 msginfo
->folder
->prefs
->default_reply_to
);
3477 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3478 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3480 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3483 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3484 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3485 extract_address(addr
);
3487 if (strcmp(ac_email
, addr
))
3488 compose_entry_append(compose
, (gchar
*)cur
->data
,
3489 COMPOSE_CC
, PREF_NONE
);
3491 debug_print("Cc address same as compose account's, ignoring\n");
3496 slist_free_strings_full(cc_list
);
3502 #define SET_ENTRY(entry, str) \
3505 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3508 #define SET_ADDRESS(type, str) \
3511 compose_entry_append(compose, str, type, PREF_NONE); \
3514 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3516 cm_return_if_fail(msginfo
!= NULL
);
3518 SET_ENTRY(subject_entry
, msginfo
->subject
);
3519 SET_ENTRY(from_name
, msginfo
->from
);
3520 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3521 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3522 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3523 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3524 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3525 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3527 compose_update_priority_menu_item(compose
);
3528 compose_update_privacy_system_menu_item(compose
, FALSE
);
3529 compose_show_first_last_header(compose
, TRUE
);
3535 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3537 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3538 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3540 GtkTextIter iter
, iter_end
;
3541 gint cur_pos
, ins_pos
;
3542 gboolean prev_autowrap
;
3543 gboolean found
= FALSE
;
3544 gboolean exists
= FALSE
;
3546 cm_return_if_fail(compose
->account
!= NULL
);
3550 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3551 G_CALLBACK(compose_changed_cb
),
3554 mark
= gtk_text_buffer_get_insert(buffer
);
3555 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3556 cur_pos
= gtk_text_iter_get_offset (&iter
);
3559 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3561 exists
= (compose
->sig_str
!= NULL
);
3564 GtkTextIter first_iter
, start_iter
, end_iter
;
3566 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3568 if (!exists
|| compose
->sig_str
[0] == '\0')
3571 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3572 compose
->signature_tag
);
3575 /* include previous \n\n */
3576 gtk_text_iter_backward_chars(&first_iter
, 1);
3577 start_iter
= first_iter
;
3578 end_iter
= first_iter
;
3580 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3581 compose
->signature_tag
);
3582 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3583 compose
->signature_tag
);
3585 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3591 g_free(compose
->sig_str
);
3592 compose
->sig_str
= account_get_signature_str(compose
->account
);
3594 cur_pos
= gtk_text_iter_get_offset(&iter
);
3596 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3597 g_free(compose
->sig_str
);
3598 compose
->sig_str
= NULL
;
3600 if (compose
->sig_inserted
== FALSE
)
3601 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3602 compose
->sig_inserted
= TRUE
;
3604 cur_pos
= gtk_text_iter_get_offset(&iter
);
3605 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3607 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3608 gtk_text_iter_forward_chars(&iter
, 1);
3609 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3610 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3612 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3613 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3616 /* put the cursor where it should be
3617 * either where the quote_fmt says, either where it was */
3618 if (compose
->set_cursor_pos
< 0)
3619 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3621 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3622 compose
->set_cursor_pos
);
3624 compose
->set_cursor_pos
= -1;
3625 gtk_text_buffer_place_cursor(buffer
, &iter
);
3626 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3627 G_CALLBACK(compose_changed_cb
),
3633 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3636 GtkTextBuffer
*buffer
;
3639 const gchar
*cur_encoding
;
3640 gchar buf
[BUFFSIZE
];
3643 gboolean prev_autowrap
;
3647 GError
*error
= NULL
;
3653 GString
*file_contents
= NULL
;
3654 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3656 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3658 /* get the size of the file we are about to insert */
3660 f
= g_file_new_for_path(file
);
3661 fi
= g_file_query_info(f
, "standard::size",
3662 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3664 if (error
!= NULL
) {
3665 g_warning(error
->message
);
3667 g_error_free(error
);
3671 ret
= g_stat(file
, &file_stat
);
3674 gchar
*shortfile
= g_path_get_basename(file
);
3675 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3677 return COMPOSE_INSERT_NO_FILE
;
3678 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3680 size
= g_file_info_get_size(fi
);
3684 size
= file_stat
.st_size
;
3687 /* ask user for confirmation if the file is large */
3688 if (prefs_common
.warn_large_insert_size
< 0 ||
3689 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3693 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3694 "in the message body. Are you sure you want to do that?"),
3695 to_human_readable(size
));
3696 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3697 _("_Insert"), NULL
, ALERTFOCUS_SECOND
, TRUE
,
3698 NULL
, ALERT_QUESTION
);
3701 /* do we ask for confirmation next time? */
3702 if (aval
& G_ALERTDISABLE
) {
3703 /* no confirmation next time, disable feature in preferences */
3704 aval
&= ~G_ALERTDISABLE
;
3705 prefs_common
.warn_large_insert
= FALSE
;
3708 /* abort file insertion if user canceled action */
3709 if (aval
!= G_ALERTALTERNATE
) {
3710 return COMPOSE_INSERT_NO_FILE
;
3716 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3717 FILE_OP_ERROR(file
, "claws_fopen");
3718 return COMPOSE_INSERT_READ_ERROR
;
3721 prev_autowrap
= compose
->autowrap
;
3722 compose
->autowrap
= FALSE
;
3724 text
= GTK_TEXT_VIEW(compose
->text
);
3725 buffer
= gtk_text_view_get_buffer(text
);
3726 mark
= gtk_text_buffer_get_insert(buffer
);
3727 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3729 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3730 G_CALLBACK(text_inserted
),
3733 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3735 file_contents
= g_string_new("");
3736 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3739 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3740 str
= g_strdup(buf
);
3742 codeconv_set_strict(TRUE
);
3743 str
= conv_codeset_strdup
3744 (buf
, cur_encoding
, CS_INTERNAL
);
3745 codeconv_set_strict(FALSE
);
3748 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3754 /* strip <CR> if DOS/Windows file,
3755 replace <CR> with <LF> if Macintosh file. */
3758 if (len
> 0 && str
[len
- 1] != '\n') {
3760 if (str
[len
] == '\r') str
[len
] = '\n';
3763 file_contents
= g_string_append(file_contents
, str
);
3767 if (result
== COMPOSE_INSERT_SUCCESS
) {
3768 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3770 compose_changed_cb(NULL
, compose
);
3771 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3772 G_CALLBACK(text_inserted
),
3774 compose
->autowrap
= prev_autowrap
;
3775 if (compose
->autowrap
)
3776 compose_wrap_all(compose
);
3779 g_string_free(file_contents
, TRUE
);
3785 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3786 const gchar
*filename
,
3787 const gchar
*content_type
,
3788 const gchar
*charset
)
3796 GtkListStore
*store
;
3798 gboolean has_binary
= FALSE
;
3800 if (!is_file_exist(file
)) {
3801 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3802 gboolean result
= FALSE
;
3803 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3804 result
= compose_attach_append(
3805 compose
, file_from_uri
,
3806 filename
, content_type
,
3809 g_free(file_from_uri
);
3812 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3815 if ((size
= get_file_size(file
)) < 0) {
3816 alertpanel_error("Can't get file size of %s\n", filename
);
3820 /* In batch mode, we allow 0-length files to be attached no questions asked */
3821 if (size
== 0 && !compose
->batch
) {
3822 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3823 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3824 GTK_STOCK_CANCEL
, _("_Attach anyway"), NULL
,
3825 ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3828 if (aval
!= G_ALERTALTERNATE
) {
3832 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3833 alertpanel_error(_("Can't read %s."), filename
);
3838 ainfo
= g_new0(AttachInfo
, 1);
3839 auto_ainfo
= g_auto_pointer_new_with_free
3840 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3841 ainfo
->file
= g_strdup(file
);
3844 ainfo
->content_type
= g_strdup(content_type
);
3845 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3847 MsgFlags flags
= {0, 0};
3849 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3850 ainfo
->encoding
= ENC_7BIT
;
3852 ainfo
->encoding
= ENC_8BIT
;
3854 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3855 if (msginfo
&& msginfo
->subject
)
3856 name
= g_strdup(msginfo
->subject
);
3858 name
= g_path_get_basename(filename
? filename
: file
);
3860 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3862 procmsg_msginfo_free(&msginfo
);
3864 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3865 ainfo
->charset
= g_strdup(charset
);
3866 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3868 ainfo
->encoding
= ENC_BASE64
;
3870 name
= g_path_get_basename(filename
? filename
: file
);
3871 ainfo
->name
= g_strdup(name
);
3875 ainfo
->content_type
= procmime_get_mime_type(file
);
3876 if (!ainfo
->content_type
) {
3877 ainfo
->content_type
=
3878 g_strdup("application/octet-stream");
3879 ainfo
->encoding
= ENC_BASE64
;
3880 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3882 procmime_get_encoding_for_text_file(file
, &has_binary
);
3884 ainfo
->encoding
= ENC_BASE64
;
3885 name
= g_path_get_basename(filename
? filename
: file
);
3886 ainfo
->name
= g_strdup(name
);
3890 if (ainfo
->name
!= NULL
3891 && !strcmp(ainfo
->name
, ".")) {
3892 g_free(ainfo
->name
);
3896 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3897 g_free(ainfo
->content_type
);
3898 ainfo
->content_type
= g_strdup("application/octet-stream");
3899 g_free(ainfo
->charset
);
3900 ainfo
->charset
= NULL
;
3903 ainfo
->size
= (goffset
)size
;
3904 size_text
= to_human_readable((goffset
)size
);
3906 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3907 (GTK_TREE_VIEW(compose
->attach_clist
)));
3909 gtk_list_store_append(store
, &iter
);
3910 gtk_list_store_set(store
, &iter
,
3911 COL_MIMETYPE
, ainfo
->content_type
,
3912 COL_SIZE
, size_text
,
3913 COL_NAME
, ainfo
->name
,
3914 COL_CHARSET
, ainfo
->charset
,
3916 COL_AUTODATA
, auto_ainfo
,
3919 g_auto_pointer_free(auto_ainfo
);
3920 compose_attach_update_label(compose
);
3924 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3926 compose
->use_signing
= use_signing
;
3927 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3930 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3932 compose
->use_encryption
= use_encryption
;
3933 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3936 #define NEXT_PART_NOT_CHILD(info) \
3938 node = info->node; \
3939 while (node->children) \
3940 node = g_node_last_child(node); \
3941 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3944 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3948 MimeInfo
*firsttext
= NULL
;
3949 MimeInfo
*encrypted
= NULL
;
3952 const gchar
*partname
= NULL
;
3954 mimeinfo
= procmime_scan_message(msginfo
);
3955 if (!mimeinfo
) return;
3957 if (mimeinfo
->node
->children
== NULL
) {
3958 procmime_mimeinfo_free_all(&mimeinfo
);
3962 /* find first content part */
3963 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3964 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3965 child
= (MimeInfo
*)child
->node
->children
->data
;
3968 if (child
->type
== MIMETYPE_TEXT
) {
3970 debug_print("First text part found\n");
3971 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3972 child
->type
== MIMETYPE_APPLICATION
&&
3973 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3974 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3977 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3978 while (child
!= NULL
) {
3981 if (child
== encrypted
) {
3982 /* skip this part of tree */
3983 NEXT_PART_NOT_CHILD(child
);
3987 if (child
->type
== MIMETYPE_MULTIPART
) {
3988 /* get the actual content */
3989 child
= procmime_mimeinfo_next(child
);
3993 if (child
== firsttext
) {
3994 child
= procmime_mimeinfo_next(child
);
3998 outfile
= procmime_get_tmp_file_name(child
);
3999 if ((err
= procmime_get_part(outfile
, child
)) < 0)
4000 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err
));
4002 gchar
*content_type
;
4004 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
4006 /* if we meet a pgp signature, we don't attach it, but
4007 * we force signing. */
4008 if ((strcmp(content_type
, "application/pgp-signature") &&
4009 strcmp(content_type
, "application/pkcs7-signature") &&
4010 strcmp(content_type
, "application/x-pkcs7-signature"))
4011 || compose
->mode
== COMPOSE_REDIRECT
) {
4012 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
4013 if (partname
== NULL
)
4014 partname
= procmime_mimeinfo_get_parameter(child
, "name");
4015 if (partname
== NULL
)
4017 compose_attach_append(compose
, outfile
,
4018 partname
, content_type
,
4019 procmime_mimeinfo_get_parameter(child
, "charset"));
4021 compose_force_signing(compose
, compose
->account
, NULL
);
4023 g_free(content_type
);
4026 NEXT_PART_NOT_CHILD(child
);
4028 procmime_mimeinfo_free_all(&mimeinfo
);
4031 #undef NEXT_PART_NOT_CHILD
4036 WAIT_FOR_INDENT_CHAR
,
4037 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4040 /* return indent length, we allow:
4041 indent characters followed by indent characters or spaces/tabs,
4042 alphabets and numbers immediately followed by indent characters,
4043 and the repeating sequences of the above
4044 If quote ends with multiple spaces, only the first one is included. */
4045 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4046 const GtkTextIter
*start
, gint
*len
)
4048 GtkTextIter iter
= *start
;
4052 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4055 gint alnum_count
= 0;
4056 gint space_count
= 0;
4059 if (prefs_common
.quote_chars
== NULL
) {
4063 while (!gtk_text_iter_ends_line(&iter
)) {
4064 wc
= gtk_text_iter_get_char(&iter
);
4065 if (g_unichar_iswide(wc
))
4067 clen
= g_unichar_to_utf8(wc
, ch
);
4071 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4072 is_space
= g_unichar_isspace(wc
);
4074 if (state
== WAIT_FOR_INDENT_CHAR
) {
4075 if (!is_indent
&& !g_unichar_isalnum(wc
))
4078 quote_len
+= alnum_count
+ space_count
+ 1;
4079 alnum_count
= space_count
= 0;
4080 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4083 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4084 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4088 else if (is_indent
) {
4089 quote_len
+= alnum_count
+ space_count
+ 1;
4090 alnum_count
= space_count
= 0;
4093 state
= WAIT_FOR_INDENT_CHAR
;
4097 gtk_text_iter_forward_char(&iter
);
4100 if (quote_len
> 0 && space_count
> 0)
4106 if (quote_len
> 0) {
4108 gtk_text_iter_forward_chars(&iter
, quote_len
);
4109 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4115 /* return >0 if the line is itemized */
4116 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4117 const GtkTextIter
*start
)
4119 GtkTextIter iter
= *start
;
4124 if (gtk_text_iter_ends_line(&iter
))
4129 wc
= gtk_text_iter_get_char(&iter
);
4130 if (!g_unichar_isspace(wc
))
4132 gtk_text_iter_forward_char(&iter
);
4133 if (gtk_text_iter_ends_line(&iter
))
4137 clen
= g_unichar_to_utf8(wc
, ch
);
4138 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4140 wc
== 0x2022 || /* BULLET */
4141 wc
== 0x2023 || /* TRIANGULAR BULLET */
4142 wc
== 0x2043 || /* HYPHEN BULLET */
4143 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4144 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4145 wc
== 0x2219 || /* BULLET OPERATOR */
4146 wc
== 0x25d8 || /* INVERSE BULLET */
4147 wc
== 0x25e6 || /* WHITE BULLET */
4148 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4149 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4150 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4151 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4152 wc
== 0x29bf /* CIRCLED BULLET */
4156 gtk_text_iter_forward_char(&iter
);
4157 if (gtk_text_iter_ends_line(&iter
))
4159 wc
= gtk_text_iter_get_char(&iter
);
4160 if (g_unichar_isspace(wc
)) {
4166 /* return the string at the start of the itemization */
4167 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4168 const GtkTextIter
*start
)
4170 GtkTextIter iter
= *start
;
4173 GString
*item_chars
= g_string_new("");
4176 if (gtk_text_iter_ends_line(&iter
))
4181 wc
= gtk_text_iter_get_char(&iter
);
4182 if (!g_unichar_isspace(wc
))
4184 gtk_text_iter_forward_char(&iter
);
4185 if (gtk_text_iter_ends_line(&iter
))
4187 g_string_append_unichar(item_chars
, wc
);
4190 str
= item_chars
->str
;
4191 g_string_free(item_chars
, FALSE
);
4195 /* return the number of spaces at a line's start */
4196 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4197 const GtkTextIter
*start
)
4199 GtkTextIter iter
= *start
;
4202 if (gtk_text_iter_ends_line(&iter
))
4206 wc
= gtk_text_iter_get_char(&iter
);
4207 if (!g_unichar_isspace(wc
))
4210 gtk_text_iter_forward_char(&iter
);
4211 if (gtk_text_iter_ends_line(&iter
))
4215 gtk_text_iter_forward_char(&iter
);
4216 if (gtk_text_iter_ends_line(&iter
))
4221 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4222 const GtkTextIter
*start
,
4223 GtkTextIter
*break_pos
,
4227 GtkTextIter iter
= *start
, line_end
= *start
;
4228 PangoLogAttr
*attrs
;
4235 gboolean can_break
= FALSE
;
4236 gboolean do_break
= FALSE
;
4237 gboolean was_white
= FALSE
;
4238 gboolean prev_dont_break
= FALSE
;
4240 gtk_text_iter_forward_to_line_end(&line_end
);
4241 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4242 len
= g_utf8_strlen(str
, -1);
4246 g_warning("compose_get_line_break_pos: len = 0!");
4250 /* g_print("breaking line: %d: %s (len = %d)\n",
4251 gtk_text_iter_get_line(&iter), str, len); */
4253 attrs
= g_new(PangoLogAttr
, len
+ 1);
4255 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4259 /* skip quote and leading spaces */
4260 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4263 wc
= g_utf8_get_char(p
);
4264 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4266 if (g_unichar_iswide(wc
))
4268 else if (*p
== '\t')
4272 p
= g_utf8_next_char(p
);
4275 for (; *p
!= '\0' && i
< len
; i
++) {
4276 PangoLogAttr
*attr
= attrs
+ i
;
4277 gunichar wc
= g_utf8_get_char(p
);
4280 /* attr->is_line_break will be false for some characters that
4281 * we want to break a line before, like '/' or ':', so we
4282 * also allow breaking on any non-wide character. The
4283 * mentioned pango attribute is still useful to decide on
4284 * line breaks when wide characters are involved. */
4285 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4286 && can_break
&& was_white
&& !prev_dont_break
)
4289 was_white
= attr
->is_white
;
4291 /* don't wrap URI */
4292 if ((uri_len
= get_uri_len(p
)) > 0) {
4294 if (pos
> 0 && col
> max_col
) {
4304 if (g_unichar_iswide(wc
)) {
4306 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4308 } else if (*p
== '\t')
4312 if (pos
> 0 && col
> max_col
) {
4317 if (*p
== '-' || *p
== '/')
4318 prev_dont_break
= TRUE
;
4320 prev_dont_break
= FALSE
;
4322 p
= g_utf8_next_char(p
);
4326 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4331 *break_pos
= *start
;
4332 gtk_text_iter_set_line_offset(break_pos
, pos
);
4337 static gboolean
compose_join_next_line(Compose
*compose
,
4338 GtkTextBuffer
*buffer
,
4340 const gchar
*quote_str
)
4342 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4343 PangoLogAttr attrs
[3];
4345 gchar
*next_quote_str
;
4348 gboolean keep_cursor
= FALSE
;
4350 if (!gtk_text_iter_forward_line(&iter_
) ||
4351 gtk_text_iter_ends_line(&iter_
)) {
4354 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4356 if ((quote_str
|| next_quote_str
) &&
4357 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4358 g_free(next_quote_str
);
4361 g_free(next_quote_str
);
4364 if (quote_len
> 0) {
4365 gtk_text_iter_forward_chars(&end
, quote_len
);
4366 if (gtk_text_iter_ends_line(&end
)) {
4371 /* don't join itemized lines */
4372 if (compose_itemized_length(buffer
, &end
) > 0) {
4376 /* don't join signature separator */
4377 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4380 /* delete quote str */
4382 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4384 /* don't join line breaks put by the user */
4386 gtk_text_iter_backward_char(&cur
);
4387 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4388 gtk_text_iter_forward_char(&cur
);
4392 gtk_text_iter_forward_char(&cur
);
4393 /* delete linebreak and extra spaces */
4394 while (gtk_text_iter_backward_char(&cur
)) {
4395 wc1
= gtk_text_iter_get_char(&cur
);
4396 if (!g_unichar_isspace(wc1
))
4401 while (!gtk_text_iter_ends_line(&cur
)) {
4402 wc1
= gtk_text_iter_get_char(&cur
);
4403 if (!g_unichar_isspace(wc1
))
4405 gtk_text_iter_forward_char(&cur
);
4408 if (!gtk_text_iter_equal(&prev
, &next
)) {
4411 mark
= gtk_text_buffer_get_insert(buffer
);
4412 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4413 if (gtk_text_iter_equal(&prev
, &cur
))
4415 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4419 /* insert space if required */
4420 gtk_text_iter_backward_char(&prev
);
4421 wc1
= gtk_text_iter_get_char(&prev
);
4422 wc2
= gtk_text_iter_get_char(&next
);
4423 gtk_text_iter_forward_char(&next
);
4424 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4425 pango_default_break(str
, -1, NULL
, attrs
, 3);
4426 if (!attrs
[1].is_line_break
||
4427 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4428 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4430 gtk_text_iter_backward_char(&iter_
);
4431 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4440 #define ADD_TXT_POS(bp_, ep_, pti_) \
4441 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4442 last = last->next; \
4443 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4444 last->next = NULL; \
4446 g_warning("alloc error scanning URIs"); \
4449 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4451 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4452 GtkTextBuffer
*buffer
;
4453 GtkTextIter iter
, break_pos
, end_of_line
;
4454 gchar
*quote_str
= NULL
;
4456 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4457 gboolean prev_autowrap
= compose
->autowrap
;
4458 gint startq_offset
= -1, noq_offset
= -1;
4459 gint uri_start
= -1, uri_stop
= -1;
4460 gint nouri_start
= -1, nouri_stop
= -1;
4461 gint num_blocks
= 0;
4462 gint quotelevel
= -1;
4463 gboolean modified
= force
;
4464 gboolean removed
= FALSE
;
4465 gboolean modified_before_remove
= FALSE
;
4467 gboolean start
= TRUE
;
4468 gint itemized_len
= 0, rem_item_len
= 0;
4469 gchar
*itemized_chars
= NULL
;
4470 gboolean item_continuation
= FALSE
;
4475 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4479 compose
->autowrap
= FALSE
;
4481 buffer
= gtk_text_view_get_buffer(text
);
4482 undo_wrapping(compose
->undostruct
, TRUE
);
4487 mark
= gtk_text_buffer_get_insert(buffer
);
4488 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4492 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4493 if (gtk_text_iter_ends_line(&iter
)) {
4494 while (gtk_text_iter_ends_line(&iter
) &&
4495 gtk_text_iter_forward_line(&iter
))
4498 while (gtk_text_iter_backward_line(&iter
)) {
4499 if (gtk_text_iter_ends_line(&iter
)) {
4500 gtk_text_iter_forward_line(&iter
);
4506 /* move to line start */
4507 gtk_text_iter_set_line_offset(&iter
, 0);
4510 itemized_len
= compose_itemized_length(buffer
, &iter
);
4512 if (!itemized_len
) {
4513 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4514 item_continuation
= TRUE
;
4518 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4520 /* go until paragraph end (empty line) */
4521 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4522 gchar
*scanpos
= NULL
;
4523 /* parse table - in order of priority */
4525 const gchar
*needle
; /* token */
4527 /* token search function */
4528 gchar
*(*search
) (const gchar
*haystack
,
4529 const gchar
*needle
);
4530 /* part parsing function */
4531 gboolean (*parse
) (const gchar
*start
,
4532 const gchar
*scanpos
,
4536 /* part to URI function */
4537 gchar
*(*build_uri
) (const gchar
*bp
,
4541 static struct table parser
[] = {
4542 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4543 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4544 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4545 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4546 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4547 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4548 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4549 {"@", strcasestr
, get_email_part
, make_email_string
}
4551 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4552 gint last_index
= PARSE_ELEMS
;
4554 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4558 if (!prev_autowrap
&& num_blocks
== 0) {
4560 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4561 G_CALLBACK(text_inserted
),
4564 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4567 uri_start
= uri_stop
= -1;
4569 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4572 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4573 if (startq_offset
== -1)
4574 startq_offset
= gtk_text_iter_get_offset(&iter
);
4575 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4576 if (quotelevel
> 2) {
4577 /* recycle colors */
4578 if (prefs_common
.recycle_quote_colors
)
4587 if (startq_offset
== -1)
4588 noq_offset
= gtk_text_iter_get_offset(&iter
);
4592 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4595 if (gtk_text_iter_ends_line(&iter
)) {
4597 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4598 prefs_common
.linewrap_len
,
4600 GtkTextIter prev
, next
, cur
;
4601 if (prev_autowrap
!= FALSE
|| force
) {
4602 compose
->automatic_break
= TRUE
;
4604 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4605 compose
->automatic_break
= FALSE
;
4606 if (itemized_len
&& compose
->autoindent
) {
4607 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4608 if (!item_continuation
)
4609 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4611 } else if (quote_str
&& wrap_quote
) {
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);
4623 /* remove trailing spaces */
4625 rem_item_len
= itemized_len
;
4626 while (compose
->autoindent
&& rem_item_len
-- > 0)
4627 gtk_text_iter_backward_char(&cur
);
4628 gtk_text_iter_backward_char(&cur
);
4631 while (!gtk_text_iter_starts_line(&cur
)) {
4634 gtk_text_iter_backward_char(&cur
);
4635 wc
= gtk_text_iter_get_char(&cur
);
4636 if (!g_unichar_isspace(wc
))
4640 if (!gtk_text_iter_equal(&prev
, &next
)) {
4641 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4643 gtk_text_iter_forward_char(&break_pos
);
4647 gtk_text_buffer_insert(buffer
, &break_pos
,
4651 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4653 /* move iter to current line start */
4654 gtk_text_iter_set_line_offset(&iter
, 0);
4661 /* move iter to next line start */
4667 if (!prev_autowrap
&& num_blocks
> 0) {
4669 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4670 G_CALLBACK(text_inserted
),
4674 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4675 gtk_text_iter_forward_char(&end_of_line
);
4677 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4679 nouri_start
= gtk_text_iter_get_offset(&iter
);
4680 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4682 walk_pos
= gtk_text_iter_get_offset(&iter
);
4683 /* FIXME: this looks phony. scanning for anything in the parse table */
4684 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4687 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4689 if (scanpos
== NULL
|| tmp
< scanpos
) {
4698 /* check if URI can be parsed */
4699 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4700 (const gchar
**)&ep
, FALSE
)
4701 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4705 strlen(parser
[last_index
].needle
);
4708 uri_start
= walk_pos
+ (bp
- o_walk
);
4709 uri_stop
= walk_pos
+ (ep
- o_walk
);
4713 gtk_text_iter_forward_line(&iter
);
4716 if (startq_offset
!= -1) {
4717 GtkTextIter startquote
, endquote
;
4718 gtk_text_buffer_get_iter_at_offset(
4719 buffer
, &startquote
, startq_offset
);
4722 switch (quotelevel
) {
4724 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4725 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4726 gtk_text_buffer_apply_tag_by_name(
4727 buffer
, "quote0", &startquote
, &endquote
);
4728 gtk_text_buffer_remove_tag_by_name(
4729 buffer
, "quote1", &startquote
, &endquote
);
4730 gtk_text_buffer_remove_tag_by_name(
4731 buffer
, "quote2", &startquote
, &endquote
);
4736 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4737 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4738 gtk_text_buffer_apply_tag_by_name(
4739 buffer
, "quote1", &startquote
, &endquote
);
4740 gtk_text_buffer_remove_tag_by_name(
4741 buffer
, "quote0", &startquote
, &endquote
);
4742 gtk_text_buffer_remove_tag_by_name(
4743 buffer
, "quote2", &startquote
, &endquote
);
4748 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4749 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4750 gtk_text_buffer_apply_tag_by_name(
4751 buffer
, "quote2", &startquote
, &endquote
);
4752 gtk_text_buffer_remove_tag_by_name(
4753 buffer
, "quote0", &startquote
, &endquote
);
4754 gtk_text_buffer_remove_tag_by_name(
4755 buffer
, "quote1", &startquote
, &endquote
);
4761 } else if (noq_offset
!= -1) {
4762 GtkTextIter startnoquote
, endnoquote
;
4763 gtk_text_buffer_get_iter_at_offset(
4764 buffer
, &startnoquote
, noq_offset
);
4767 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4768 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4769 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4770 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4771 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4772 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4773 gtk_text_buffer_remove_tag_by_name(
4774 buffer
, "quote0", &startnoquote
, &endnoquote
);
4775 gtk_text_buffer_remove_tag_by_name(
4776 buffer
, "quote1", &startnoquote
, &endnoquote
);
4777 gtk_text_buffer_remove_tag_by_name(
4778 buffer
, "quote2", &startnoquote
, &endnoquote
);
4784 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4785 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4786 gtk_text_buffer_get_iter_at_offset(
4787 buffer
, &nouri_start_iter
, nouri_start
);
4788 gtk_text_buffer_get_iter_at_offset(
4789 buffer
, &nouri_end_iter
, nouri_stop
);
4790 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4791 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4792 gtk_text_buffer_remove_tag_by_name(
4793 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4794 modified_before_remove
= modified
;
4799 if (uri_start
>= 0 && uri_stop
> 0) {
4800 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4801 gtk_text_buffer_get_iter_at_offset(
4802 buffer
, &uri_start_iter
, uri_start
);
4803 gtk_text_buffer_get_iter_at_offset(
4804 buffer
, &uri_end_iter
, uri_stop
);
4805 back
= uri_end_iter
;
4806 gtk_text_iter_backward_char(&back
);
4807 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4808 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4809 gtk_text_buffer_apply_tag_by_name(
4810 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4812 if (removed
&& !modified_before_remove
) {
4818 /* debug_print("not modified, out after %d lines\n", lines); */
4822 /* debug_print("modified, out after %d lines\n", lines); */
4824 g_free(itemized_chars
);
4827 undo_wrapping(compose
->undostruct
, FALSE
);
4828 compose
->autowrap
= prev_autowrap
;
4833 void compose_action_cb(void *data
)
4835 Compose
*compose
= (Compose
*)data
;
4836 compose_wrap_all(compose
);
4839 static void compose_wrap_all(Compose
*compose
)
4841 compose_wrap_all_full(compose
, FALSE
);
4844 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4846 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4847 GtkTextBuffer
*buffer
;
4849 gboolean modified
= TRUE
;
4851 buffer
= gtk_text_view_get_buffer(text
);
4853 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4855 undo_wrapping(compose
->undostruct
, TRUE
);
4857 while (!gtk_text_iter_is_end(&iter
) && modified
)
4858 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4860 undo_wrapping(compose
->undostruct
, FALSE
);
4864 static void compose_set_title(Compose
*compose
)
4870 edited
= compose
->modified
? _(" [Edited]") : "";
4872 subject
= gtk_editable_get_chars(
4873 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4875 #ifndef GENERIC_UMPC
4876 if (subject
&& strlen(subject
))
4877 str
= g_strdup_printf(_("%s - Compose message%s"),
4880 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4882 str
= g_strdup(_("Compose message"));
4885 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4891 * compose_current_mail_account:
4893 * Find a current mail account (the currently selected account, or the
4894 * default account, if a news account is currently selected). If a
4895 * mail account cannot be found, display an error message.
4897 * Return value: Mail account, or NULL if not found.
4899 static PrefsAccount
*
4900 compose_current_mail_account(void)
4904 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4907 ac
= account_get_default();
4908 if (!ac
|| ac
->protocol
== A_NNTP
) {
4909 alertpanel_error(_("Account for sending mail is not specified.\n"
4910 "Please select a mail account before sending."));
4917 #define QUOTE_IF_REQUIRED(out, str) \
4919 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4923 len = strlen(str) + 3; \
4924 if ((__tmp = alloca(len)) == NULL) { \
4925 g_warning("can't allocate memory"); \
4926 g_string_free(header, TRUE); \
4929 g_snprintf(__tmp, len, "\"%s\"", str); \
4934 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4935 g_warning("can't allocate memory"); \
4936 g_string_free(header, TRUE); \
4939 strcpy(__tmp, str); \
4945 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4947 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4951 len = strlen(str) + 3; \
4952 if ((__tmp = alloca(len)) == NULL) { \
4953 g_warning("can't allocate memory"); \
4956 g_snprintf(__tmp, len, "\"%s\"", str); \
4961 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4962 g_warning("can't allocate memory"); \
4965 strcpy(__tmp, str); \
4971 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4974 gchar
*from
= NULL
, *header
= NULL
;
4975 ComposeHeaderEntry
*header_entry
;
4978 cm_return_if_fail(account
!= NULL
);
4980 compose
->account
= account
;
4981 if (account
->name
&& *account
->name
) {
4983 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4984 qbuf
= escape_internal_quotes(buf
, '"');
4985 from
= g_strdup_printf("%s <%s>",
4986 qbuf
, account
->address
);
4989 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4991 from
= g_strdup_printf("<%s>",
4993 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4998 compose_set_title(compose
);
5000 compose_activate_privacy_system(compose
, account
, FALSE
);
5002 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
5003 compose
->mode
!= COMPOSE_REDIRECT
)
5004 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
5006 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
5007 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
5008 compose
->mode
!= COMPOSE_REDIRECT
)
5009 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
5011 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
5013 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
5014 undo_block(compose
->undostruct
);
5015 compose_insert_sig(compose
, TRUE
);
5016 undo_unblock(compose
->undostruct
);
5019 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5020 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5021 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5022 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5024 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5025 if (account
->protocol
== A_NNTP
) {
5026 if (!strcmp(header
, _("To:")))
5027 combobox_select_by_text(
5028 GTK_COMBO_BOX(header_entry
->combo
),
5031 if (!strcmp(header
, _("Newsgroups:")))
5032 combobox_select_by_text(
5033 GTK_COMBO_BOX(header_entry
->combo
),
5041 /* use account's dict info if set */
5042 if (compose
->gtkaspell
) {
5043 if (account
->enable_default_dictionary
)
5044 gtkaspell_change_dict(compose
->gtkaspell
,
5045 account
->default_dictionary
, FALSE
);
5046 if (account
->enable_default_alt_dictionary
)
5047 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5048 account
->default_alt_dictionary
);
5049 if (account
->enable_default_dictionary
5050 || account
->enable_default_alt_dictionary
)
5051 compose_spell_menu_changed(compose
);
5056 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5057 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5058 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5059 gboolean recipient_found
= FALSE
;
5063 /* free to and newsgroup list */
5064 slist_free_strings_full(compose
->to_list
);
5065 compose
->to_list
= NULL
;
5067 slist_free_strings_full(compose
->newsgroup_list
);
5068 compose
->newsgroup_list
= NULL
;
5070 /* search header entries for to and newsgroup entries */
5071 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5074 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5075 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5078 if (entry
[0] != '\0') {
5079 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5080 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5081 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5082 recipient_found
= TRUE
;
5085 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5086 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5087 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5088 recipient_found
= TRUE
;
5095 return recipient_found
;
5098 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5100 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5101 gboolean found_other
= FALSE
;
5103 /* search header entries for to and newsgroup entries */
5104 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5107 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5108 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5111 if (strcmp(entry
, compose
->account
->auto_cc
)
5112 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5123 if (compose
->batch
) {
5124 gtk_widget_show_all(compose
->window
);
5126 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5127 prefs_common_translated_header_name("Cc"));
5128 aval
= alertpanel(_("Send"),
5130 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5132 if (aval
!= G_ALERTALTERNATE
)
5136 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5137 gboolean found_other
= FALSE
;
5139 /* search header entries for to and newsgroup entries */
5140 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5143 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5144 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5147 if (strcmp(entry
, compose
->account
->auto_bcc
)
5148 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5160 if (compose
->batch
) {
5161 gtk_widget_show_all(compose
->window
);
5163 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5164 prefs_common_translated_header_name("Bcc"));
5165 aval
= alertpanel(_("Send"),
5167 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5169 if (aval
!= G_ALERTALTERNATE
)
5176 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5180 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5181 if (compose
->batch
) {
5182 gtk_widget_show_all(compose
->window
);
5184 alertpanel_error(_("Recipient is not specified."));
5188 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5192 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5193 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5194 if (*str
== '\0' && check_everything
== TRUE
&&
5195 compose
->mode
!= COMPOSE_REDIRECT
) {
5199 message
= g_strdup_printf(_("Subject is empty. %s"),
5200 compose
->sending
?_("Send it anyway?"):
5201 _("Queue it anyway?"));
5203 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5204 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5205 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5207 if (aval
& G_ALERTDISABLE
) {
5208 aval
&= ~G_ALERTDISABLE
;
5209 prefs_common
.warn_empty_subj
= FALSE
;
5211 if (aval
!= G_ALERTALTERNATE
)
5216 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5217 && check_everything
== TRUE
) {
5221 /* count To and Cc recipients */
5222 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5226 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5227 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5230 if ((entry
[0] != '\0') &&
5231 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5232 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5238 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5242 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5243 compose
->sending
?_("Send it anyway?"):
5244 _("Queue it anyway?"));
5246 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5247 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5248 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5250 if (aval
& G_ALERTDISABLE
) {
5251 aval
&= ~G_ALERTDISABLE
;
5252 prefs_common
.warn_sending_many_recipients_num
= 0;
5254 if (aval
!= G_ALERTALTERNATE
)
5259 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5265 static void _display_queue_error(ComposeQueueResult val
)
5268 case COMPOSE_QUEUE_SUCCESS
:
5270 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5271 alertpanel_error(_("Could not queue message."));
5273 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5274 alertpanel_error(_("Could not queue message:\n\n%s."),
5277 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5278 alertpanel_error(_("Could not queue message for sending:\n\n"
5279 "Signature failed: %s"),
5280 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5282 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5283 alertpanel_error(_("Could not queue message for sending:\n\n"
5284 "Encryption failed: %s"),
5285 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5287 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5288 alertpanel_error(_("Could not queue message for sending:\n\n"
5289 "Charset conversion failed."));
5291 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5292 alertpanel_error(_("Could not queue message for sending:\n\n"
5293 "Couldn't get recipient encryption key."));
5295 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5296 debug_print("signing cancelled\n");
5299 /* unhandled error */
5300 debug_print("oops, unhandled compose_queue() return value %d\n",
5306 gint
compose_send(Compose
*compose
)
5309 FolderItem
*folder
= NULL
;
5310 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5311 gchar
*msgpath
= NULL
;
5312 gboolean discard_window
= FALSE
;
5313 gchar
*errstr
= NULL
;
5314 gchar
*tmsgid
= NULL
;
5315 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5316 gboolean queued_removed
= FALSE
;
5318 if (prefs_common
.send_dialog_invisible
5319 || compose
->batch
== TRUE
)
5320 discard_window
= TRUE
;
5322 compose_allow_user_actions (compose
, FALSE
);
5323 compose
->sending
= TRUE
;
5325 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5326 if (compose
->batch
) {
5327 gtk_widget_show_all(compose
->window
);
5333 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5335 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5336 if (compose
->batch
) {
5337 gtk_widget_show_all(compose
->window
);
5340 _display_queue_error(val
);
5345 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5346 if (discard_window
) {
5347 compose
->sending
= FALSE
;
5348 compose_close(compose
);
5349 /* No more compose access in the normal codepath
5350 * after this point! */
5355 alertpanel_error(_("The message was queued but could not be "
5356 "sent.\nUse \"Send queued messages\" from "
5357 "the main window to retry."));
5358 if (!discard_window
) {
5365 if (msgpath
== NULL
) {
5366 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5367 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5370 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5371 claws_unlink(msgpath
);
5374 if (!discard_window
) {
5376 if (!queued_removed
)
5377 folder_item_remove_msg(folder
, msgnum
);
5378 folder_item_scan(folder
);
5380 /* make sure we delete that */
5381 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5383 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5384 folder_item_remove_msg(folder
, tmp
->msgnum
);
5385 procmsg_msginfo_free(&tmp
);
5392 if (!queued_removed
)
5393 folder_item_remove_msg(folder
, msgnum
);
5394 folder_item_scan(folder
);
5396 /* make sure we delete that */
5397 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5399 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5400 folder_item_remove_msg(folder
, tmp
->msgnum
);
5401 procmsg_msginfo_free(&tmp
);
5404 if (!discard_window
) {
5405 compose
->sending
= FALSE
;
5406 compose_allow_user_actions (compose
, TRUE
);
5407 compose_close(compose
);
5411 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5412 "the main window to retry."), errstr
);
5415 alertpanel_error_log(_("The message was queued but could not be "
5416 "sent.\nUse \"Send queued messages\" from "
5417 "the main window to retry."));
5419 if (!discard_window
) {
5428 toolbar_main_set_sensitive(mainwin
);
5429 main_window_set_menu_sensitive(mainwin
);
5435 compose_allow_user_actions (compose
, TRUE
);
5436 compose
->sending
= FALSE
;
5437 compose
->modified
= TRUE
;
5438 toolbar_main_set_sensitive(mainwin
);
5439 main_window_set_menu_sensitive(mainwin
);
5444 static gboolean
compose_use_attach(Compose
*compose
)
5446 GtkTreeModel
*model
= gtk_tree_view_get_model
5447 (GTK_TREE_VIEW(compose
->attach_clist
));
5448 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5451 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5454 gchar buf
[BUFFSIZE
];
5456 gboolean first_to_address
;
5457 gboolean first_cc_address
;
5459 ComposeHeaderEntry
*headerentry
;
5460 const gchar
*headerentryname
;
5461 const gchar
*cc_hdr
;
5462 const gchar
*to_hdr
;
5463 gboolean err
= FALSE
;
5465 debug_print("Writing redirect header\n");
5467 cc_hdr
= prefs_common_translated_header_name("Cc:");
5468 to_hdr
= prefs_common_translated_header_name("To:");
5470 first_to_address
= TRUE
;
5471 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5472 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5473 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5475 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5476 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5477 Xstrdup_a(str
, entstr
, return -1);
5479 if (str
[0] != '\0') {
5480 compose_convert_header
5481 (compose
, buf
, sizeof(buf
), str
,
5482 strlen("Resent-To") + 2, TRUE
);
5484 if (first_to_address
) {
5485 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5486 first_to_address
= FALSE
;
5488 err
|= (fprintf(fp
, ",") < 0);
5490 err
|= (fprintf(fp
, "%s", buf
) < 0);
5494 if (!first_to_address
) {
5495 err
|= (fprintf(fp
, "\n") < 0);
5498 first_cc_address
= TRUE
;
5499 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5500 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5501 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5503 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5504 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5505 Xstrdup_a(str
, strg
, return -1);
5507 if (str
[0] != '\0') {
5508 compose_convert_header
5509 (compose
, buf
, sizeof(buf
), str
,
5510 strlen("Resent-Cc") + 2, TRUE
);
5512 if (first_cc_address
) {
5513 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5514 first_cc_address
= FALSE
;
5516 err
|= (fprintf(fp
, ",") < 0);
5518 err
|= (fprintf(fp
, "%s", buf
) < 0);
5522 if (!first_cc_address
) {
5523 err
|= (fprintf(fp
, "\n") < 0);
5526 return (err
? -1:0);
5529 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5531 gchar date
[RFC822_DATE_BUFFSIZE
];
5532 gchar buf
[BUFFSIZE
];
5534 const gchar
*entstr
;
5535 /* struct utsname utsbuf; */
5536 gboolean err
= FALSE
;
5538 cm_return_val_if_fail(fp
!= NULL
, -1);
5539 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5540 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5543 if (prefs_common
.hide_timezone
)
5544 get_rfc822_date_hide_tz(date
, sizeof(date
));
5546 get_rfc822_date(date
, sizeof(date
));
5547 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5550 if (compose
->account
->name
&& *compose
->account
->name
) {
5551 compose_convert_header
5552 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5553 strlen("From: "), TRUE
);
5554 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5555 buf
, compose
->account
->address
) < 0);
5557 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5560 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5561 if (*entstr
!= '\0') {
5562 Xstrdup_a(str
, entstr
, return -1);
5565 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5566 strlen("Subject: "), FALSE
);
5567 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5571 /* Resent-Message-ID */
5572 if (compose
->account
->gen_msgid
) {
5573 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5574 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5576 g_free(compose
->msgid
);
5577 compose
->msgid
= addr
;
5579 compose
->msgid
= NULL
;
5582 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5585 /* separator between header and body */
5586 err
|= (claws_fputs("\n", fp
) == EOF
);
5588 return (err
? -1:0);
5591 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5596 gchar rewrite_buf
[BUFFSIZE
];
5598 gboolean skip
= FALSE
;
5599 gboolean err
= FALSE
;
5600 gchar
*not_included
[]={
5601 "Return-Path:", "Delivered-To:", "Received:",
5602 "Subject:", "X-UIDL:", "AF:",
5603 "NF:", "PS:", "SRH:",
5604 "SFN:", "DSR:", "MID:",
5605 "CFG:", "PT:", "S:",
5606 "RQ:", "SSV:", "NSV:",
5607 "SSH:", "R:", "MAID:",
5608 "NAID:", "RMID:", "FMID:",
5609 "SCF:", "RRCPT:", "NG:",
5610 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5611 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5612 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5613 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5614 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5619 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5620 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5624 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5626 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5627 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5628 strlen(not_included
[i
])) == 0) {
5638 if (claws_fputs(buf
, fdest
) == -1) {
5644 if (!prefs_common
.redirect_keep_from
) {
5645 if (g_ascii_strncasecmp(buf
, "From:",
5646 strlen("From:")) == 0) {
5647 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5648 if (compose
->account
->name
5649 && *compose
->account
->name
) {
5650 gchar buffer
[BUFFSIZE
];
5652 compose_convert_header
5653 (compose
, buffer
, sizeof(buffer
),
5654 compose
->account
->name
,
5657 err
|= (fprintf(fdest
, "%s <%s>",
5659 compose
->account
->address
) < 0);
5661 err
|= (fprintf(fdest
, "%s",
5662 compose
->account
->address
) < 0);
5663 err
|= (claws_fputs(")", fdest
) == EOF
);
5669 if (claws_fputs("\n", fdest
) == -1)
5676 if (compose_redirect_write_headers(compose
, fdest
))
5679 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5680 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5694 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5696 GtkTextBuffer
*buffer
;
5697 GtkTextIter start
, end
, tmp
;
5698 gchar
*chars
, *tmp_enc_file
, *content
;
5700 const gchar
*out_codeset
;
5701 EncodingType encoding
= ENC_UNKNOWN
;
5702 MimeInfo
*mimemsg
, *mimetext
;
5704 const gchar
*src_codeset
= CS_INTERNAL
;
5705 gchar
*from_addr
= NULL
;
5706 gchar
*from_name
= NULL
;
5709 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5710 attach_parts
= TRUE
;
5712 /* We're sending the message, generate a Message-ID
5714 if (compose
->msgid
== NULL
&&
5715 compose
->account
->gen_msgid
) {
5716 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5720 /* create message MimeInfo */
5721 mimemsg
= procmime_mimeinfo_new();
5722 mimemsg
->type
= MIMETYPE_MESSAGE
;
5723 mimemsg
->subtype
= g_strdup("rfc822");
5724 mimemsg
->content
= MIMECONTENT_MEM
;
5725 mimemsg
->tmp
= TRUE
; /* must free content later */
5726 mimemsg
->data
.mem
= compose_get_header(compose
);
5728 /* Create text part MimeInfo */
5729 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5730 gtk_text_buffer_get_end_iter(buffer
, &end
);
5733 /* We make sure that there is a newline at the end. */
5734 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5735 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5736 if (*chars
!= '\n') {
5737 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5742 /* get all composed text */
5743 gtk_text_buffer_get_start_iter(buffer
, &start
);
5744 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5746 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5748 if (!out_codeset
&& is_ascii_str(chars
)) {
5749 out_codeset
= CS_US_ASCII
;
5750 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5751 is_ascii_str(chars
)) {
5752 out_codeset
= CS_US_ASCII
;
5753 encoding
= ENC_7BIT
;
5757 gchar
*test_conv_global_out
= NULL
;
5758 gchar
*test_conv_reply
= NULL
;
5760 /* automatic mode. be automatic. */
5761 codeconv_set_strict(TRUE
);
5763 out_codeset
= conv_get_outgoing_charset_str();
5765 debug_print("trying to convert to %s\n", out_codeset
);
5766 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5769 if (!test_conv_global_out
&& compose
->orig_charset
5770 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5771 out_codeset
= compose
->orig_charset
;
5772 debug_print("failure; trying to convert to %s\n", out_codeset
);
5773 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5776 if (!test_conv_global_out
&& !test_conv_reply
) {
5778 out_codeset
= CS_INTERNAL
;
5779 debug_print("failure; finally using %s\n", out_codeset
);
5781 g_free(test_conv_global_out
);
5782 g_free(test_conv_reply
);
5783 codeconv_set_strict(FALSE
);
5786 if (encoding
== ENC_UNKNOWN
) {
5787 if (prefs_common
.encoding_method
== CTE_BASE64
)
5788 encoding
= ENC_BASE64
;
5789 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5790 encoding
= ENC_QUOTED_PRINTABLE
;
5791 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5792 encoding
= ENC_8BIT
;
5794 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5797 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5798 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5800 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5801 codeconv_set_strict(TRUE
);
5802 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5803 codeconv_set_strict(FALSE
);
5808 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5809 "to the specified %s charset.\n"
5810 "Send it as %s?"), out_codeset
, src_codeset
);
5811 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
,
5812 _("_Send"), NULL
, ALERTFOCUS_SECOND
, FALSE
,
5816 if (aval
!= G_ALERTALTERNATE
) {
5818 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5821 out_codeset
= src_codeset
;
5827 out_codeset
= src_codeset
;
5832 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5833 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5834 strstr(buf
, "\nFrom ") != NULL
) {
5835 encoding
= ENC_QUOTED_PRINTABLE
;
5839 mimetext
= procmime_mimeinfo_new();
5840 mimetext
->content
= MIMECONTENT_MEM
;
5841 mimetext
->tmp
= TRUE
; /* must free content later */
5842 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5843 * and free the data, which we need later. */
5844 mimetext
->data
.mem
= g_strdup(buf
);
5845 mimetext
->type
= MIMETYPE_TEXT
;
5846 mimetext
->subtype
= g_strdup("plain");
5847 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5848 g_strdup(out_codeset
));
5850 /* protect trailing spaces when signing message */
5851 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5852 privacy_system_can_sign(compose
->privacy_system
)) {
5853 encoding
= ENC_QUOTED_PRINTABLE
;
5857 debug_print("main text: %Id bytes encoded as %s in %d\n",
5859 debug_print("main text: %zd bytes encoded as %s in %d\n",
5861 strlen(buf
), out_codeset
, encoding
);
5863 /* check for line length limit */
5864 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5865 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5866 check_line_length(buf
, 1000, &line
) < 0) {
5869 msg
= g_strdup_printf
5870 (_("Line %d exceeds the line length limit (998 bytes).\n"
5871 "The contents of the message might be broken on the way to the delivery.\n"
5873 "Send it anyway?"), line
+ 1);
5874 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
,
5877 if (aval
!= G_ALERTALTERNATE
) {
5879 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5883 if (encoding
!= ENC_UNKNOWN
)
5884 procmime_encode_content(mimetext
, encoding
);
5886 /* append attachment parts */
5887 if (compose_use_attach(compose
) && attach_parts
) {
5888 MimeInfo
*mimempart
;
5889 gchar
*boundary
= NULL
;
5890 mimempart
= procmime_mimeinfo_new();
5891 mimempart
->content
= MIMECONTENT_EMPTY
;
5892 mimempart
->type
= MIMETYPE_MULTIPART
;
5893 mimempart
->subtype
= g_strdup("mixed");
5897 boundary
= generate_mime_boundary(NULL
);
5898 } while (strstr(buf
, boundary
) != NULL
);
5900 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5903 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5905 g_node_append(mimempart
->node
, mimetext
->node
);
5906 g_node_append(mimemsg
->node
, mimempart
->node
);
5908 if (compose_add_attachments(compose
, mimempart
) < 0)
5909 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5911 g_node_append(mimemsg
->node
, mimetext
->node
);
5915 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5916 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5917 /* extract name and address */
5918 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5919 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5920 *(strrchr(from_addr
, '>')) = '\0';
5921 from_name
= g_strdup(spec
);
5922 *(strrchr(from_name
, '<')) = '\0';
5929 /* sign message if sending */
5930 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5931 privacy_system_can_sign(compose
->privacy_system
))
5932 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5933 compose
->account
, from_addr
)) {
5936 if (!privacy_peek_error())
5937 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5939 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5944 if (compose
->use_encryption
) {
5945 if (compose
->encdata
!= NULL
&&
5946 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5948 /* First, write an unencrypted copy and save it to outbox, if
5949 * user wants that. */
5950 if (compose
->account
->save_encrypted_as_clear_text
) {
5951 debug_print("saving sent message unencrypted...\n");
5952 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5954 claws_fclose(tmpfp
);
5956 /* fp now points to a file with headers written,
5957 * let's make a copy. */
5959 content
= file_read_stream_to_str(fp
);
5961 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5964 /* Now write the unencrypted body. */
5965 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5966 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5967 claws_fclose(tmpfp
);
5969 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5971 outbox
= folder_get_default_outbox();
5973 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5974 claws_unlink(tmp_enc_file
);
5976 g_warning("Can't open file '%s'", tmp_enc_file
);
5979 g_warning("couldn't get tempfile");
5982 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5983 debug_print("Couldn't encrypt mime structure: %s.\n",
5984 privacy_get_error());
5985 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5990 procmime_write_mimeinfo(mimemsg
, fp
);
5992 procmime_mimeinfo_free_all(&mimemsg
);
5997 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5999 GtkTextBuffer
*buffer
;
6000 GtkTextIter start
, end
;
6005 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
6006 FILE_OP_ERROR(file
, "claws_fopen");
6010 /* chmod for security */
6011 if (change_file_mode_rw(fp
, file
) < 0) {
6012 FILE_OP_ERROR(file
, "chmod");
6013 g_warning("can't change file mode");
6016 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
6017 gtk_text_buffer_get_start_iter(buffer
, &start
);
6018 gtk_text_buffer_get_end_iter(buffer
, &end
);
6019 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6021 chars
= conv_codeset_strdup
6022 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6031 len
= strlen(chars
);
6032 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6033 FILE_OP_ERROR(file
, "claws_fwrite");
6042 if (claws_safe_fclose(fp
) == EOF
) {
6043 FILE_OP_ERROR(file
, "claws_fclose");
6050 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6053 MsgInfo
*msginfo
= compose
->targetinfo
;
6055 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6056 if (!msginfo
) return -1;
6058 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6061 item
= msginfo
->folder
;
6062 cm_return_val_if_fail(item
!= NULL
, -1);
6064 if (procmsg_msg_exist(msginfo
) &&
6065 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6066 folder_has_parent_of_type(item
, F_DRAFT
)
6067 || msginfo
== compose
->autosaved_draft
)) {
6068 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6069 g_warning("can't remove the old message");
6072 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6079 static void compose_remove_draft(Compose
*compose
)
6082 MsgInfo
*msginfo
= compose
->targetinfo
;
6083 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6085 if (procmsg_msg_exist(msginfo
)) {
6086 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6091 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6092 gboolean remove_reedit_target
)
6094 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6097 static gboolean
compose_warn_encryption(Compose
*compose
)
6099 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6100 AlertValue val
= G_ALERTALTERNATE
;
6102 if (warning
== NULL
)
6105 val
= alertpanel_full(_("Encryption warning"), warning
,
6106 GTK_STOCK_CANCEL
, _("C_ontinue"), NULL
, ALERTFOCUS_SECOND
,
6107 TRUE
, NULL
, ALERT_WARNING
);
6108 if (val
& G_ALERTDISABLE
) {
6109 val
&= ~G_ALERTDISABLE
;
6110 if (val
== G_ALERTALTERNATE
)
6111 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6115 if (val
== G_ALERTALTERNATE
) {
6122 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6123 gchar
**msgpath
, gboolean perform_checks
,
6124 gboolean remove_reedit_target
)
6131 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6132 gboolean err
= FALSE
;
6134 debug_print("queueing message...\n");
6135 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6137 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6138 if (compose
->batch
) {
6139 gtk_widget_show_all(compose
->window
);
6141 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6144 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6145 g_warning("can't get recipient list.");
6146 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6149 if (compose
->to_list
) {
6150 if (compose
->account
->protocol
!= A_NNTP
)
6151 mailac
= compose
->account
;
6152 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
6153 mailac
= cur_account
;
6154 else if (!(mailac
= compose_current_mail_account())) {
6155 alertpanel_error(_("No account for sending mails available!"));
6156 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6160 if (compose
->newsgroup_list
) {
6161 if (compose
->account
->protocol
== A_NNTP
)
6162 newsac
= compose
->account
;
6164 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6165 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6169 /* write queue header */
6170 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6171 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6172 debug_print("queuing to %s\n", tmp
);
6173 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6174 FILE_OP_ERROR(tmp
, "claws_fopen");
6176 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6179 if (change_file_mode_rw(fp
, tmp
) < 0) {
6180 FILE_OP_ERROR(tmp
, "chmod");
6181 g_warning("can't change file mode");
6184 /* queueing variables */
6185 err
|= (fprintf(fp
, "AF:\n") < 0);
6186 err
|= (fprintf(fp
, "NF:0\n") < 0);
6187 err
|= (fprintf(fp
, "PS:10\n") < 0);
6188 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6189 err
|= (fprintf(fp
, "SFN:\n") < 0);
6190 err
|= (fprintf(fp
, "DSR:\n") < 0);
6192 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6194 err
|= (fprintf(fp
, "MID:\n") < 0);
6195 err
|= (fprintf(fp
, "CFG:\n") < 0);
6196 err
|= (fprintf(fp
, "PT:0\n") < 0);
6197 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6198 err
|= (fprintf(fp
, "RQ:\n") < 0);
6200 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6202 err
|= (fprintf(fp
, "SSV:\n") < 0);
6204 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6206 err
|= (fprintf(fp
, "NSV:\n") < 0);
6207 err
|= (fprintf(fp
, "SSH:\n") < 0);
6208 /* write recipient list */
6209 if (compose
->to_list
) {
6210 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6211 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6213 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6214 err
|= (fprintf(fp
, "\n") < 0);
6216 /* write newsgroup list */
6217 if (compose
->newsgroup_list
) {
6218 err
|= (fprintf(fp
, "NG:") < 0);
6219 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6220 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6221 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6222 err
|= (fprintf(fp
, "\n") < 0);
6226 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6228 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6231 if (compose
->privacy_system
!= NULL
) {
6232 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6233 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6234 if (compose
->use_encryption
) {
6235 if (!compose_warn_encryption(compose
)) {
6239 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6241 if (mailac
&& mailac
->encrypt_to_self
) {
6242 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6243 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6244 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6245 g_slist_free(tmp_list
);
6247 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6249 if (compose
->encdata
!= NULL
) {
6250 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6251 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6252 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6253 compose
->encdata
) < 0);
6254 } /* else we finally dont want to encrypt */
6256 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6257 /* and if encdata was null, it means there's been a problem in
6260 g_warning("failed to write queue message");
6264 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6269 /* Save copy folder */
6270 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6271 gchar
*savefolderid
;
6273 savefolderid
= compose_get_save_to(compose
);
6274 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6275 g_free(savefolderid
);
6277 /* Save copy folder */
6278 if (compose
->return_receipt
) {
6279 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6281 /* Message-ID of message replying to */
6282 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6283 gchar
*folderid
= NULL
;
6285 if (compose
->replyinfo
->folder
)
6286 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6287 if (folderid
== NULL
)
6288 folderid
= g_strdup("NULL");
6290 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6293 /* Message-ID of message forwarding to */
6294 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6295 gchar
*folderid
= NULL
;
6297 if (compose
->fwdinfo
->folder
)
6298 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6299 if (folderid
== NULL
)
6300 folderid
= g_strdup("NULL");
6302 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6306 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6307 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6309 /* end of headers */
6310 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6312 if (compose
->redirect_filename
!= NULL
) {
6313 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6317 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6321 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6329 g_warning("failed to write queue message");
6333 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6335 if (claws_safe_fclose(fp
) == EOF
) {
6336 FILE_OP_ERROR(tmp
, "claws_fclose");
6339 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6342 if (item
&& *item
) {
6345 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6348 g_warning("can't find queue folder");
6351 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6353 folder_item_scan(queue
);
6354 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6355 g_warning("can't queue the message");
6358 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6361 if (msgpath
== NULL
) {
6367 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6368 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6370 procmsg_msginfo_change_flags(mi
,
6371 compose
->targetinfo
->flags
.perm_flags
,
6372 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6375 g_slist_free(mi
->tags
);
6376 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6377 procmsg_msginfo_free(&mi
);
6381 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6382 compose_remove_reedit_target(compose
, FALSE
);
6385 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6390 return COMPOSE_QUEUE_SUCCESS
;
6393 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6396 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6401 GError
*error
= NULL
;
6406 gchar
*type
, *subtype
;
6407 GtkTreeModel
*model
;
6410 model
= gtk_tree_view_get_model(tree_view
);
6412 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6415 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6417 if (!is_file_exist(ainfo
->file
)) {
6418 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6419 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6420 _("Cancel sending"), _("Ignore attachment"), NULL
,
6421 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6423 if (val
== G_ALERTDEFAULT
) {
6429 f
= g_file_new_for_path(ainfo
->file
);
6430 fi
= g_file_query_info(f
, "standard::size",
6431 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6432 if (error
!= NULL
) {
6433 g_warning(error
->message
);
6434 g_error_free(error
);
6438 size
= g_file_info_get_size(fi
);
6442 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6444 size
= statbuf
.st_size
;
6447 mimepart
= procmime_mimeinfo_new();
6448 mimepart
->content
= MIMECONTENT_FILE
;
6449 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6450 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6451 mimepart
->offset
= 0;
6452 mimepart
->length
= size
;
6454 type
= g_strdup(ainfo
->content_type
);
6456 if (!strchr(type
, '/')) {
6458 type
= g_strdup("application/octet-stream");
6461 subtype
= strchr(type
, '/') + 1;
6462 *(subtype
- 1) = '\0';
6463 mimepart
->type
= procmime_get_media_type(type
);
6464 mimepart
->subtype
= g_strdup(subtype
);
6467 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6468 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6469 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6470 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6471 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6472 /* Text parts with no name come from multipart/alternative
6473 * forwards. Make sure the recipient won't look at the
6474 * original HTML part by mistake. */
6475 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6476 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6480 g_hash_table_insert(mimepart
->typeparameters
,
6481 g_strdup("charset"), g_strdup(ainfo
->charset
));
6483 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6484 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6485 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6486 g_hash_table_insert(mimepart
->typeparameters
,
6487 g_strdup("name"), g_strdup(ainfo
->name
));
6488 g_hash_table_insert(mimepart
->dispositionparameters
,
6489 g_strdup("filename"), g_strdup(ainfo
->name
));
6490 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6493 if (mimepart
->type
== MIMETYPE_MESSAGE
6494 || mimepart
->type
== MIMETYPE_MULTIPART
)
6495 ainfo
->encoding
= ENC_BINARY
;
6496 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6497 if (ainfo
->encoding
== ENC_7BIT
)
6498 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6499 else if (ainfo
->encoding
== ENC_8BIT
)
6500 ainfo
->encoding
= ENC_BASE64
;
6503 procmime_encode_content(mimepart
, ainfo
->encoding
);
6505 g_node_append(parent
->node
, mimepart
->node
);
6506 } while (gtk_tree_model_iter_next(model
, &iter
));
6511 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6513 GSList
*list
= NULL
, *item
= NULL
;
6514 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6516 list
= address_list_append_with_comments(list
, str
);
6517 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6518 gchar
*spec
= item
->data
;
6519 gchar
*endofname
= strstr(spec
, " <");
6520 if (endofname
!= NULL
) {
6523 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6524 qqname
= escape_internal_quotes(qname
, '"');
6526 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6527 gchar
*addr
= g_strdup(endofname
);
6528 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6529 faddr
= g_strconcat(name
, addr
, NULL
);
6532 debug_print("new auto-quoted address: '%s'\n", faddr
);
6536 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6538 result
= g_strconcat(result
,
6540 (faddr
!= NULL
)? faddr
: spec
,
6543 if (faddr
!= NULL
) {
6548 slist_free_strings_full(list
);
6553 #define IS_IN_CUSTOM_HEADER(header) \
6554 (compose->account->add_customhdr && \
6555 custom_header_find(compose->account->customhdr_list, header) != NULL)
6557 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6559 /* return the untranslated header name, if header_name is a known
6560 header name, in either its translated or untranslated form, with
6561 or without trailing colon. otherwise, returns header_name. */
6562 gchar
*translated_header_name
;
6563 gchar
*translated_header_name_wcolon
;
6564 const gchar
*untranslated_header_name
;
6565 const gchar
*untranslated_header_name_wcolon
;
6568 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6570 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6571 untranslated_header_name
= HEADERS
[i
].header_name
;
6572 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6574 translated_header_name
= gettext(untranslated_header_name
);
6575 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6577 if (!strcmp(header_name
, untranslated_header_name
) ||
6578 !strcmp(header_name
, translated_header_name
)) {
6579 return untranslated_header_name
;
6581 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6582 !strcmp(header_name
, translated_header_name_wcolon
)) {
6583 return untranslated_header_name_wcolon
;
6587 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6591 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6593 const gchar
*fieldname
,
6594 const gchar
*seperator
)
6596 gchar
*str
, *fieldname_w_colon
;
6597 gboolean add_field
= FALSE
;
6599 ComposeHeaderEntry
*headerentry
;
6600 const gchar
*headerentryname
;
6601 const gchar
*trans_fieldname
;
6604 if (IS_IN_CUSTOM_HEADER(fieldname
))
6607 debug_print("Adding %s-fields\n", fieldname
);
6609 fieldstr
= g_string_sized_new(64);
6611 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6612 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6614 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6615 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6616 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6618 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6619 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6621 str
= compose_quote_list_of_addresses(ustr
);
6623 if (str
!= NULL
&& str
[0] != '\0') {
6625 g_string_append(fieldstr
, seperator
);
6626 g_string_append(fieldstr
, str
);
6635 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6636 compose_convert_header
6637 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6638 strlen(fieldname
) + 2, TRUE
);
6639 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6643 g_free(fieldname_w_colon
);
6644 g_string_free(fieldstr
, TRUE
);
6649 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6651 GString
*sh_header
= g_string_new(" ");
6653 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6655 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6656 ComposeHeaderEntry
*headerentry
;
6659 gchar
*headername_wcolon
;
6660 const gchar
*headername_trans
;
6662 gboolean standard_header
= FALSE
;
6664 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6666 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6668 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6673 if (!strstr(tmp
, ":")) {
6674 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6675 headername
= g_strdup(tmp
);
6677 headername_wcolon
= g_strdup(tmp
);
6678 headername
= g_strdup(strtok(tmp
, ":"));
6682 string
= std_headers
;
6683 while (*string
!= NULL
) {
6684 headername_trans
= prefs_common_translated_header_name(*string
);
6685 if (!strcmp(headername_trans
, headername_wcolon
))
6686 standard_header
= TRUE
;
6689 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6690 g_string_append_printf(sh_header
, "%s ", headername
);
6692 g_free(headername_wcolon
);
6694 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6695 return g_string_free(sh_header
, FALSE
);
6698 static gchar
*compose_get_header(Compose
*compose
)
6700 gchar date
[RFC822_DATE_BUFFSIZE
];
6701 gchar buf
[BUFFSIZE
];
6702 const gchar
*entry_str
;
6706 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6708 gchar
*from_name
= NULL
, *from_address
= NULL
;
6711 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6712 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6714 header
= g_string_sized_new(64);
6717 if (prefs_common
.hide_timezone
)
6718 get_rfc822_date_hide_tz(date
, sizeof(date
));
6720 get_rfc822_date(date
, sizeof(date
));
6721 g_string_append_printf(header
, "Date: %s\n", date
);
6725 if (compose
->account
->name
&& *compose
->account
->name
) {
6727 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6728 tmp
= g_strdup_printf("%s <%s>",
6729 buf
, compose
->account
->address
);
6731 tmp
= g_strdup_printf("%s",
6732 compose
->account
->address
);
6734 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6735 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6737 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6738 from_address
= g_strdup(compose
->account
->address
);
6740 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6741 /* extract name and address */
6742 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6743 from_address
= g_strdup(strrchr(spec
, '<')+1);
6744 *(strrchr(from_address
, '>')) = '\0';
6745 from_name
= g_strdup(spec
);
6746 *(strrchr(from_name
, '<')) = '\0';
6749 from_address
= g_strdup(spec
);
6756 if (from_name
&& *from_name
) {
6758 compose_convert_header
6759 (compose
, buf
, sizeof(buf
), from_name
,
6760 strlen("From: "), TRUE
);
6761 QUOTE_IF_REQUIRED(name
, buf
);
6762 qname
= escape_internal_quotes(name
, '"');
6764 g_string_append_printf(header
, "From: %s <%s>\n",
6765 qname
, from_address
);
6766 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6767 compose
->return_receipt
) {
6768 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6769 strlen("Disposition-Notification-To: "),
6771 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6776 g_string_append_printf(header
, "From: %s\n", from_address
);
6777 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6778 compose
->return_receipt
)
6779 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6783 g_free(from_address
);
6786 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6789 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6792 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6796 * If this account is a NNTP account remove Bcc header from
6797 * message body since it otherwise will be publicly shown
6799 if (compose
->account
->protocol
!= A_NNTP
)
6800 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6803 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6805 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6808 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6809 strlen("Subject: "), FALSE
);
6810 g_string_append_printf(header
, "Subject: %s\n", buf
);
6816 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6817 g_string_append_printf(header
, "Message-ID: <%s>\n",
6821 if (compose
->remove_references
== FALSE
) {
6823 if (compose
->inreplyto
&& compose
->to_list
)
6824 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6827 if (compose
->references
)
6828 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6832 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6835 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6838 if (compose
->account
->organization
&&
6839 strlen(compose
->account
->organization
) &&
6840 !IS_IN_CUSTOM_HEADER("Organization")) {
6841 compose_convert_header(compose
, buf
, sizeof(buf
),
6842 compose
->account
->organization
,
6843 strlen("Organization: "), FALSE
);
6844 g_string_append_printf(header
, "Organization: %s\n", buf
);
6847 /* Program version and system info */
6848 if (compose
->account
->gen_xmailer
&&
6849 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6850 !compose
->newsgroup_list
) {
6851 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6853 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6856 if (compose
->account
->gen_xmailer
&&
6857 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6858 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6860 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6864 /* custom headers */
6865 if (compose
->account
->add_customhdr
) {
6868 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6870 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6872 if (custom_header_is_allowed(chdr
->name
)
6873 && chdr
->value
!= NULL
6874 && *(chdr
->value
) != '\0') {
6875 compose_convert_header
6876 (compose
, buf
, sizeof(buf
),
6878 strlen(chdr
->name
) + 2, FALSE
);
6879 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6884 /* Automatic Faces and X-Faces */
6885 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6886 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6888 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6889 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6891 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6892 g_string_append_printf(header
, "Face: %s\n", buf
);
6894 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6895 g_string_append_printf(header
, "Face: %s\n", buf
);
6899 switch (compose
->priority
) {
6900 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6901 "X-Priority: 1 (Highest)\n");
6903 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6904 "X-Priority: 2 (High)\n");
6906 case PRIORITY_NORMAL
: break;
6907 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6908 "X-Priority: 4 (Low)\n");
6910 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6911 "X-Priority: 5 (Lowest)\n");
6913 default: debug_print("compose: priority unknown : %d\n",
6917 /* get special headers */
6918 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6919 ComposeHeaderEntry
*headerentry
;
6922 gchar
*headername_wcolon
;
6923 const gchar
*headername_trans
;
6926 gboolean standard_header
= FALSE
;
6928 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6930 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6932 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6937 if (!strstr(tmp
, ":")) {
6938 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6939 headername
= g_strdup(tmp
);
6941 headername_wcolon
= g_strdup(tmp
);
6942 headername
= g_strdup(strtok(tmp
, ":"));
6946 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6947 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6948 subst_char(headervalue
, '\r', ' ');
6949 subst_char(headervalue
, '\n', ' ');
6950 g_strstrip(headervalue
);
6951 if (*headervalue
!= '\0') {
6952 string
= std_headers
;
6953 while (*string
!= NULL
&& !standard_header
) {
6954 headername_trans
= prefs_common_translated_header_name(*string
);
6955 /* support mixed translated and untranslated headers */
6956 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6957 standard_header
= TRUE
;
6960 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6961 /* store untranslated header name */
6962 g_string_append_printf(header
, "%s %s\n",
6963 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6967 g_free(headername_wcolon
);
6971 g_string_free(header
, FALSE
);
6976 #undef IS_IN_CUSTOM_HEADER
6978 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6979 gint header_len
, gboolean addr_field
)
6981 gchar
*tmpstr
= NULL
;
6982 const gchar
*out_codeset
= NULL
;
6984 cm_return_if_fail(src
!= NULL
);
6985 cm_return_if_fail(dest
!= NULL
);
6987 if (len
< 1) return;
6989 tmpstr
= g_strdup(src
);
6991 subst_char(tmpstr
, '\n', ' ');
6992 subst_char(tmpstr
, '\r', ' ');
6995 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6996 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6997 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
7002 codeconv_set_strict(TRUE
);
7003 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7004 conv_get_charset_str(compose
->out_encoding
));
7005 codeconv_set_strict(FALSE
);
7007 if (!dest
|| *dest
== '\0') {
7008 gchar
*test_conv_global_out
= NULL
;
7009 gchar
*test_conv_reply
= NULL
;
7011 /* automatic mode. be automatic. */
7012 codeconv_set_strict(TRUE
);
7014 out_codeset
= conv_get_outgoing_charset_str();
7016 debug_print("trying to convert to %s\n", out_codeset
);
7017 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7020 if (!test_conv_global_out
&& compose
->orig_charset
7021 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7022 out_codeset
= compose
->orig_charset
;
7023 debug_print("failure; trying to convert to %s\n", out_codeset
);
7024 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7027 if (!test_conv_global_out
&& !test_conv_reply
) {
7029 out_codeset
= CS_INTERNAL
;
7030 debug_print("finally using %s\n", out_codeset
);
7032 g_free(test_conv_global_out
);
7033 g_free(test_conv_reply
);
7034 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7036 codeconv_set_strict(FALSE
);
7041 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7045 cm_return_if_fail(user_data
!= NULL
);
7047 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7048 g_strstrip(address
);
7049 if (*address
!= '\0') {
7050 gchar
*name
= procheader_get_fromname(address
);
7051 extract_address(address
);
7052 #ifndef USE_ALT_ADDRBOOK
7053 addressbook_add_contact(name
, address
, NULL
, NULL
);
7055 debug_print("%s: %s\n", name
, address
);
7056 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7057 debug_print( "addressbook_add_contact - added\n" );
7064 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7066 GtkWidget
*menuitem
;
7069 cm_return_if_fail(menu
!= NULL
);
7070 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7072 menuitem
= gtk_separator_menu_item_new();
7073 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7074 gtk_widget_show(menuitem
);
7076 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7077 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7079 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7080 g_strstrip(address
);
7081 if (*address
== '\0') {
7082 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7085 g_signal_connect(G_OBJECT(menuitem
), "activate",
7086 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7087 gtk_widget_show(menuitem
);
7090 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7093 if (strcmp(header
, "")) {
7094 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7098 void compose_add_extra_header_entries(GtkListStore
*model
)
7102 gchar buf
[BUFFSIZE
];
7105 if (extra_headers
== NULL
) {
7106 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7107 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7108 debug_print("extra headers file not found\n");
7109 goto extra_headers_done
;
7111 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7112 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7113 while (lastc
>= 0 && buf
[lastc
] != ':')
7114 buf
[lastc
--] = '\0';
7115 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7116 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7117 if (custom_header_is_allowed(buf
)) {
7119 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7122 g_message("disallowed extra header line: %s\n", buf
);
7126 g_message("invalid extra header line: %s\n", buf
);
7132 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7133 extra_headers
= g_slist_reverse(extra_headers
);
7135 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7139 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7141 LdapServer
*server
= (LdapServer
*)data
;
7142 gboolean
*enable
= (gboolean
*)user_data
;
7144 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7145 server
->searchFlag
= *enable
;
7149 static void compose_create_header_entry(Compose
*compose
)
7151 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7158 const gchar
*header
= NULL
;
7159 ComposeHeaderEntry
*headerentry
;
7160 gboolean standard_header
= FALSE
;
7161 GtkListStore
*model
;
7164 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7166 /* Combo box model */
7167 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7168 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7170 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7172 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7174 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7175 COMPOSE_NEWSGROUPS
);
7176 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7178 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7179 COMPOSE_FOLLOWUPTO
);
7180 compose_add_extra_header_entries(model
);
7183 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7184 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7185 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7186 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7187 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7188 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7189 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7190 G_CALLBACK(compose_grab_focus_cb
), compose
);
7191 gtk_widget_show(combo
);
7193 /* Putting only the combobox child into focus chain of its parent causes
7194 * the parent to be skipped when changing focus via Tab or Shift+Tab.
7195 * This eliminates need to pres Tab twice in order to really get from the
7196 * combobox to next widget. */
7198 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
7199 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
7202 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
7203 compose
->header_nextrow
, compose
->header_nextrow
+1,
7204 GTK_SHRINK
, GTK_FILL
, 0, 0);
7205 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7206 const gchar
*last_header_entry
= gtk_entry_get_text(
7207 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7209 while (*string
!= NULL
) {
7210 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7211 standard_header
= TRUE
;
7214 if (standard_header
)
7215 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7217 if (!compose
->header_last
|| !standard_header
) {
7218 switch(compose
->account
->protocol
) {
7220 header
= prefs_common_translated_header_name("Newsgroups:");
7223 header
= prefs_common_translated_header_name("To:");
7228 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7230 gtk_editable_set_editable(
7231 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7232 prefs_common
.type_any_header
);
7234 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7235 G_CALLBACK(compose_grab_focus_cb
), compose
);
7237 /* Entry field with cleanup button */
7238 button
= gtk_button_new();
7239 gtk_button_set_image(GTK_BUTTON(button
),
7240 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
7241 gtk_widget_show(button
);
7242 CLAWS_SET_TIP(button
,
7243 _("Delete entry contents"));
7244 entry
= gtk_entry_new();
7245 gtk_widget_show(entry
);
7246 CLAWS_SET_TIP(entry
,
7247 _("Use <tab> to autocomplete from addressbook"));
7248 hbox
= gtk_hbox_new (FALSE
, 0);
7249 gtk_widget_show(hbox
);
7250 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7251 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7252 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
7253 compose
->header_nextrow
, compose
->header_nextrow
+1,
7254 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
7256 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7257 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7259 g_signal_connect(G_OBJECT(entry
), "changed",
7260 G_CALLBACK(compose_headerentry_changed_cb
),
7262 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7263 G_CALLBACK(compose_grab_focus_cb
), compose
);
7265 g_signal_connect(G_OBJECT(button
), "clicked",
7266 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7270 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7271 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7272 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7273 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7274 G_CALLBACK(compose_header_drag_received_cb
),
7276 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7277 G_CALLBACK(compose_drag_drop
),
7279 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7280 G_CALLBACK(compose_entry_popup_extend
),
7284 #ifndef PASSWORD_CRYPTO_OLD
7285 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7286 if (pwd_servers
!= NULL
&& master_passphrase() == NULL
) {
7287 gboolean enable
= FALSE
;
7288 debug_print("Master passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7289 /* Temporarily disable password-protected LDAP servers,
7290 * because user did not provide a master passphrase.
7291 * We can safely enable searchFlag on all servers in this list
7292 * later, since addrindex_get_password_protected_ldap_servers()
7293 * includes servers which have it enabled initially. */
7294 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7295 compose
->passworded_ldap_servers
= pwd_servers
;
7297 #endif /* PASSWORD_CRYPTO_OLD */
7298 #endif /* USE_LDAP */
7300 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7302 headerentry
->compose
= compose
;
7303 headerentry
->combo
= combo
;
7304 headerentry
->entry
= entry
;
7305 headerentry
->button
= button
;
7306 headerentry
->hbox
= hbox
;
7307 headerentry
->headernum
= compose
->header_nextrow
;
7308 headerentry
->type
= PREF_NONE
;
7310 compose
->header_nextrow
++;
7311 compose
->header_last
= headerentry
;
7312 compose
->header_list
=
7313 g_slist_append(compose
->header_list
,
7317 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7318 gchar
*text
, ComposePrefType pref_type
)
7320 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7321 gchar
*tmp
= g_strdup(text
), *email
;
7322 gboolean replyto_hdr
;
7324 replyto_hdr
= (!strcasecmp(header
,
7325 prefs_common_translated_header_name("Reply-To:")) ||
7327 prefs_common_translated_header_name("Followup-To:")) ||
7329 prefs_common_translated_header_name("In-Reply-To:")));
7331 extract_address(tmp
);
7332 email
= g_utf8_strdown(tmp
, -1);
7334 if (replyto_hdr
== FALSE
&&
7335 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7337 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7338 header
, text
, (gint
) pref_type
);
7344 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7345 gtk_entry_set_text(GTK_ENTRY(
7346 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7348 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7349 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7350 last_header
->type
= pref_type
;
7352 if (replyto_hdr
== FALSE
)
7353 g_hash_table_insert(compose
->email_hashtable
, email
,
7354 GUINT_TO_POINTER(1));
7361 static void compose_destroy_headerentry(Compose
*compose
,
7362 ComposeHeaderEntry
*headerentry
)
7364 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7367 extract_address(text
);
7368 email
= g_utf8_strdown(text
, -1);
7369 g_hash_table_remove(compose
->email_hashtable
, email
);
7373 gtk_widget_destroy(headerentry
->combo
);
7374 gtk_widget_destroy(headerentry
->entry
);
7375 gtk_widget_destroy(headerentry
->button
);
7376 gtk_widget_destroy(headerentry
->hbox
);
7377 g_free(headerentry
);
7380 static void compose_remove_header_entries(Compose
*compose
)
7383 for (list
= compose
->header_list
; list
; list
= list
->next
)
7384 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7386 compose
->header_last
= NULL
;
7387 g_slist_free(compose
->header_list
);
7388 compose
->header_list
= NULL
;
7389 compose
->header_nextrow
= 1;
7390 compose_create_header_entry(compose
);
7393 static GtkWidget
*compose_create_header(Compose
*compose
)
7395 GtkWidget
*from_optmenu_hbox
;
7396 GtkWidget
*header_table_main
;
7397 GtkWidget
*header_scrolledwin
;
7398 GtkWidget
*header_table
;
7400 /* parent with account selection and from header */
7401 header_table_main
= gtk_table_new(2, 2, FALSE
);
7402 gtk_widget_show(header_table_main
);
7403 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7405 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7406 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7407 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7409 /* child with header labels and entries */
7410 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7411 gtk_widget_show(header_scrolledwin
);
7412 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7414 header_table
= gtk_table_new(2, 2, FALSE
);
7415 gtk_widget_show(header_table
);
7416 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7417 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7418 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7419 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7420 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7422 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7423 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7425 compose
->header_table
= header_table
;
7426 compose
->header_list
= NULL
;
7427 compose
->header_nextrow
= 0;
7429 compose_create_header_entry(compose
);
7431 compose
->table
= NULL
;
7433 return header_table_main
;
7436 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7438 Compose
*compose
= (Compose
*)data
;
7439 GdkEventButton event
;
7442 event
.time
= gtk_get_current_event_time();
7444 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7447 static GtkWidget
*compose_create_attach(Compose
*compose
)
7449 GtkWidget
*attach_scrwin
;
7450 GtkWidget
*attach_clist
;
7452 GtkListStore
*store
;
7453 GtkCellRenderer
*renderer
;
7454 GtkTreeViewColumn
*column
;
7455 GtkTreeSelection
*selection
;
7457 /* attachment list */
7458 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7459 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7460 GTK_POLICY_AUTOMATIC
,
7461 GTK_POLICY_AUTOMATIC
);
7462 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7464 store
= gtk_list_store_new(N_ATTACH_COLS
,
7470 G_TYPE_AUTO_POINTER
,
7472 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7473 (GTK_TREE_MODEL(store
)));
7474 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7475 g_object_unref(store
);
7477 renderer
= gtk_cell_renderer_text_new();
7478 column
= gtk_tree_view_column_new_with_attributes
7479 (_("Mime type"), renderer
, "text",
7480 COL_MIMETYPE
, NULL
);
7481 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7483 renderer
= gtk_cell_renderer_text_new();
7484 column
= gtk_tree_view_column_new_with_attributes
7485 (_("Size"), renderer
, "text",
7487 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7489 renderer
= gtk_cell_renderer_text_new();
7490 column
= gtk_tree_view_column_new_with_attributes
7491 (_("Name"), renderer
, "text",
7493 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7495 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7496 prefs_common
.use_stripes_everywhere
);
7497 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7498 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7500 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7501 G_CALLBACK(attach_selected
), compose
);
7502 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7503 G_CALLBACK(attach_button_pressed
), compose
);
7504 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7505 G_CALLBACK(popup_attach_button_pressed
), compose
);
7506 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7507 G_CALLBACK(attach_key_pressed
), compose
);
7510 gtk_drag_dest_set(attach_clist
,
7511 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7512 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7513 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7514 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7515 G_CALLBACK(compose_attach_drag_received_cb
),
7517 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7518 G_CALLBACK(compose_drag_drop
),
7521 compose
->attach_scrwin
= attach_scrwin
;
7522 compose
->attach_clist
= attach_clist
;
7524 return attach_scrwin
;
7527 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7529 static GtkWidget
*compose_create_others(Compose
*compose
)
7532 GtkWidget
*savemsg_checkbtn
;
7533 GtkWidget
*savemsg_combo
;
7534 GtkWidget
*savemsg_select
;
7537 gchar
*folderidentifier
;
7539 /* Table for settings */
7540 table
= gtk_table_new(3, 1, FALSE
);
7541 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7542 gtk_widget_show(table
);
7543 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7546 /* Save Message to folder */
7547 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7548 gtk_widget_show(savemsg_checkbtn
);
7549 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7550 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7551 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7554 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7555 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7556 compose
->savemsg_combo
= savemsg_combo
;
7557 gtk_widget_show(savemsg_combo
);
7559 if (prefs_common
.compose_save_to_history
)
7560 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7561 prefs_common
.compose_save_to_history
);
7562 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7563 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7564 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7565 G_CALLBACK(compose_grab_focus_cb
), compose
);
7566 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7567 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7568 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7570 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7571 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7572 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7573 (compose
->account
, F_OUTBOX
));
7574 compose_set_save_to(compose
, folderidentifier
);
7575 g_free(folderidentifier
);
7578 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7579 gtk_widget_show(savemsg_select
);
7580 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7581 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7582 G_CALLBACK(compose_savemsg_select_cb
),
7588 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7593 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7594 _("Select folder to save message to"));
7597 path
= folder_item_get_identifier(dest
);
7599 compose_set_save_to(compose
, path
);
7603 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7604 GdkAtom clip
, GtkTextIter
*insert_place
);
7607 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7611 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7613 if (event
->button
== 3) {
7615 GtkTextIter sel_start
, sel_end
;
7616 gboolean stuff_selected
;
7618 /* move the cursor to allow GtkAspell to check the word
7619 * under the mouse */
7620 if (event
->x
&& event
->y
) {
7621 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7622 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7624 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7627 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7628 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7631 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7633 &sel_start
, &sel_end
);
7635 gtk_text_buffer_place_cursor (buffer
, &iter
);
7636 /* reselect stuff */
7638 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7639 gtk_text_buffer_select_range(buffer
,
7640 &sel_start
, &sel_end
);
7642 return FALSE
; /* pass the event so that the right-click goes through */
7645 if (event
->button
== 2) {
7650 /* get the middle-click position to paste at the correct place */
7651 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7652 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7654 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7657 entry_paste_clipboard(compose
, text
,
7658 prefs_common
.linewrap_pastes
,
7659 GDK_SELECTION_PRIMARY
, &iter
);
7667 static void compose_spell_menu_changed(void *data
)
7669 Compose
*compose
= (Compose
*)data
;
7671 GtkWidget
*menuitem
;
7672 GtkWidget
*parent_item
;
7673 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7676 if (compose
->gtkaspell
== NULL
)
7679 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7680 "/Menu/Spelling/Options");
7682 /* setting the submenu removes /Spelling/Options from the factory
7683 * so we need to save it */
7685 if (parent_item
== NULL
) {
7686 parent_item
= compose
->aspell_options_menu
;
7687 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7689 compose
->aspell_options_menu
= parent_item
;
7691 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7693 spell_menu
= g_slist_reverse(spell_menu
);
7694 for (items
= spell_menu
;
7695 items
; items
= items
->next
) {
7696 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7697 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7698 gtk_widget_show(GTK_WIDGET(menuitem
));
7700 g_slist_free(spell_menu
);
7702 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7703 gtk_widget_show(parent_item
);
7706 static void compose_dict_changed(void *data
)
7708 Compose
*compose
= (Compose
*) data
;
7710 if(!compose
->gtkaspell
)
7712 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7715 gtkaspell_highlight_all(compose
->gtkaspell
);
7716 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7720 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7722 Compose
*compose
= (Compose
*)data
;
7723 GdkEventButton event
;
7726 event
.time
= gtk_get_current_event_time();
7730 return text_clicked(compose
->text
, &event
, compose
);
7733 static gboolean compose_force_window_origin
= TRUE
;
7734 static Compose
*compose_create(PrefsAccount
*account
,
7743 GtkWidget
*handlebox
;
7745 GtkWidget
*notebook
;
7747 GtkWidget
*attach_hbox
;
7748 GtkWidget
*attach_lab1
;
7749 GtkWidget
*attach_lab2
;
7754 GtkWidget
*subject_hbox
;
7755 GtkWidget
*subject_frame
;
7756 GtkWidget
*subject_entry
;
7760 GtkWidget
*edit_vbox
;
7761 GtkWidget
*ruler_hbox
;
7763 GtkWidget
*scrolledwin
;
7765 GtkTextBuffer
*buffer
;
7766 GtkClipboard
*clipboard
;
7768 UndoMain
*undostruct
;
7770 GtkWidget
*popupmenu
;
7771 GtkWidget
*tmpl_menu
;
7772 GtkActionGroup
*action_group
= NULL
;
7775 GtkAspell
* gtkaspell
= NULL
;
7778 static GdkGeometry geometry
;
7780 cm_return_val_if_fail(account
!= NULL
, NULL
);
7782 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7783 &default_header_bgcolor
);
7784 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER
],
7785 &default_header_color
);
7787 debug_print("Creating compose window...\n");
7788 compose
= g_new0(Compose
, 1);
7790 compose
->batch
= batch
;
7791 compose
->account
= account
;
7792 compose
->folder
= folder
;
7794 compose
->mutex
= cm_mutex_new();
7795 compose
->set_cursor_pos
= -1;
7797 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7799 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7800 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7801 prefs_common
.compose_height
);
7803 if (!geometry
.max_width
) {
7804 geometry
.max_width
= gdk_screen_width();
7805 geometry
.max_height
= gdk_screen_height();
7808 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7809 &geometry
, GDK_HINT_MAX_SIZE
);
7810 if (!geometry
.min_width
) {
7811 geometry
.min_width
= 600;
7812 geometry
.min_height
= 440;
7814 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7815 &geometry
, GDK_HINT_MIN_SIZE
);
7817 #ifndef GENERIC_UMPC
7818 if (compose_force_window_origin
)
7819 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7820 prefs_common
.compose_y
);
7822 g_signal_connect(G_OBJECT(window
), "delete_event",
7823 G_CALLBACK(compose_delete_cb
), compose
);
7824 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7825 gtk_widget_realize(window
);
7827 gtkut_widget_set_composer_icon(window
);
7829 vbox
= gtk_vbox_new(FALSE
, 0);
7830 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7832 compose
->ui_manager
= gtk_ui_manager_new();
7833 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7834 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7835 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7836 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7837 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7838 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7839 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7840 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7841 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7842 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7844 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7846 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7847 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7849 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7851 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7852 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7853 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7859 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7860 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7863 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7867 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7868 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7882 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7886 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7888 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7890 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7891 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7895 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7896 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7897 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7898 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7900 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7906 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7908 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7918 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7919 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7921 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7925 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7926 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7927 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7933 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7934 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7935 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7941 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7944 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7946 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7947 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7948 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7949 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7950 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7952 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7954 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7955 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7956 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7957 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7958 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7960 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7961 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
)
7962 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
)
7963 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7965 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7967 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7968 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
)
7969 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
)
7971 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7973 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7974 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
)
7975 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7977 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7978 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
)
7979 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7981 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7983 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7984 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
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7986 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7987 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7988 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7990 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7991 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
)
7992 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
)
7993 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7994 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7996 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
8001 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
8003 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
8004 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
8005 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
)
8007 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
8008 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
8009 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
8013 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
8014 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8015 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8016 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8017 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8018 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8021 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8023 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8024 gtk_widget_show_all(menubar
);
8026 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8027 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8029 if (prefs_common
.toolbar_detachable
) {
8030 handlebox
= gtk_handle_box_new();
8032 handlebox
= gtk_hbox_new(FALSE
, 0);
8034 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8036 gtk_widget_realize(handlebox
);
8037 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8040 vbox2
= gtk_vbox_new(FALSE
, 2);
8041 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8042 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8045 notebook
= gtk_notebook_new();
8046 gtk_widget_show(notebook
);
8048 /* header labels and entries */
8049 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8050 compose_create_header(compose
),
8051 gtk_label_new_with_mnemonic(_("Hea_der")));
8052 /* attachment list */
8053 attach_hbox
= gtk_hbox_new(FALSE
, 0);
8054 gtk_widget_show(attach_hbox
);
8056 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8057 gtk_widget_show(attach_lab1
);
8058 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8060 attach_lab2
= gtk_label_new("");
8061 gtk_widget_show(attach_lab2
);
8062 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8064 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8065 compose_create_attach(compose
),
8068 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8069 compose_create_others(compose
),
8070 gtk_label_new_with_mnemonic(_("Othe_rs")));
8073 subject_hbox
= gtk_hbox_new(FALSE
, 0);
8074 gtk_widget_show(subject_hbox
);
8076 subject_frame
= gtk_frame_new(NULL
);
8077 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8078 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8079 gtk_widget_show(subject_frame
);
8081 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
8082 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8083 gtk_widget_show(subject
);
8085 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8086 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8087 gtk_widget_show(label
);
8090 subject_entry
= claws_spell_entry_new();
8092 subject_entry
= gtk_entry_new();
8094 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8095 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8096 G_CALLBACK(compose_grab_focus_cb
), compose
);
8097 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8098 gtk_widget_show(subject_entry
);
8099 compose
->subject_entry
= subject_entry
;
8100 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8102 edit_vbox
= gtk_vbox_new(FALSE
, 0);
8104 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8107 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
8108 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8110 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8111 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8112 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8116 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8117 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8118 GTK_POLICY_AUTOMATIC
,
8119 GTK_POLICY_AUTOMATIC
);
8120 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8122 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8124 text
= gtk_text_view_new();
8125 if (prefs_common
.show_compose_margin
) {
8126 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8127 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8129 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8130 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8131 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8132 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8133 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8135 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8136 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8137 G_CALLBACK(compose_edit_size_alloc
),
8139 g_signal_connect(G_OBJECT(buffer
), "changed",
8140 G_CALLBACK(compose_changed_cb
), compose
);
8141 g_signal_connect(G_OBJECT(text
), "grab_focus",
8142 G_CALLBACK(compose_grab_focus_cb
), compose
);
8143 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8144 G_CALLBACK(text_inserted
), compose
);
8145 g_signal_connect(G_OBJECT(text
), "button_press_event",
8146 G_CALLBACK(text_clicked
), compose
);
8147 g_signal_connect(G_OBJECT(text
), "popup-menu",
8148 G_CALLBACK(compose_popup_menu
), compose
);
8149 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8150 G_CALLBACK(compose_changed_cb
), compose
);
8151 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8152 G_CALLBACK(compose_subject_entry_activated
), compose
);
8155 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8156 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8157 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8158 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8159 G_CALLBACK(compose_insert_drag_received_cb
),
8161 g_signal_connect(G_OBJECT(text
), "drag-drop",
8162 G_CALLBACK(compose_drag_drop
),
8164 g_signal_connect(G_OBJECT(text
), "key-press-event",
8165 G_CALLBACK(completion_set_focus_to_subject
),
8167 gtk_widget_show_all(vbox
);
8169 /* pane between attach clist and text */
8170 paned
= gtk_vpaned_new();
8171 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
8172 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8173 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8174 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8175 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8176 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8178 gtk_widget_show_all(paned
);
8181 if (prefs_common
.textfont
) {
8182 PangoFontDescription
*font_desc
;
8184 font_desc
= pango_font_description_from_string
8185 (prefs_common
.textfont
);
8187 gtk_widget_modify_font(text
, font_desc
);
8188 pango_font_description_free(font_desc
);
8192 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8193 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8194 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8195 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8196 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8197 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8198 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8199 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8201 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8203 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8204 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8205 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8207 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8209 undostruct
= undo_init(text
);
8210 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8213 address_completion_start(window
);
8215 compose
->window
= window
;
8216 compose
->vbox
= vbox
;
8217 compose
->menubar
= menubar
;
8218 compose
->handlebox
= handlebox
;
8220 compose
->vbox2
= vbox2
;
8222 compose
->paned
= paned
;
8224 compose
->attach_label
= attach_lab2
;
8226 compose
->notebook
= notebook
;
8227 compose
->edit_vbox
= edit_vbox
;
8228 compose
->ruler_hbox
= ruler_hbox
;
8229 compose
->ruler
= ruler
;
8230 compose
->scrolledwin
= scrolledwin
;
8231 compose
->text
= text
;
8233 compose
->focused_editable
= NULL
;
8235 compose
->popupmenu
= popupmenu
;
8237 compose
->tmpl_menu
= tmpl_menu
;
8239 compose
->mode
= mode
;
8240 compose
->rmode
= mode
;
8242 compose
->targetinfo
= NULL
;
8243 compose
->replyinfo
= NULL
;
8244 compose
->fwdinfo
= NULL
;
8246 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8247 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8249 compose
->replyto
= NULL
;
8251 compose
->bcc
= NULL
;
8252 compose
->followup_to
= NULL
;
8254 compose
->ml_post
= NULL
;
8256 compose
->inreplyto
= NULL
;
8257 compose
->references
= NULL
;
8258 compose
->msgid
= NULL
;
8259 compose
->boundary
= NULL
;
8261 compose
->autowrap
= prefs_common
.autowrap
;
8262 compose
->autoindent
= prefs_common
.auto_indent
;
8263 compose
->use_signing
= FALSE
;
8264 compose
->use_encryption
= FALSE
;
8265 compose
->privacy_system
= NULL
;
8266 compose
->encdata
= NULL
;
8268 compose
->modified
= FALSE
;
8270 compose
->return_receipt
= FALSE
;
8272 compose
->to_list
= NULL
;
8273 compose
->newsgroup_list
= NULL
;
8275 compose
->undostruct
= undostruct
;
8277 compose
->sig_str
= NULL
;
8279 compose
->exteditor_file
= NULL
;
8280 compose
->exteditor_pid
= -1;
8281 compose
->exteditor_tag
= -1;
8282 compose
->exteditor_socket
= NULL
;
8283 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8285 compose
->folder_update_callback_id
=
8286 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8287 compose_update_folder_hook
,
8288 (gpointer
) compose
);
8291 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8292 if (mode
!= COMPOSE_REDIRECT
) {
8293 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8294 strcmp(prefs_common
.dictionary
, "")) {
8295 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8296 prefs_common
.alt_dictionary
,
8297 conv_get_locale_charset_str(),
8298 prefs_common
.color
[COL_MISSPELLED
],
8299 prefs_common
.check_while_typing
,
8300 prefs_common
.recheck_when_changing_dict
,
8301 prefs_common
.use_alternate
,
8302 prefs_common
.use_both_dicts
,
8303 GTK_TEXT_VIEW(text
),
8304 GTK_WINDOW(compose
->window
),
8305 compose_dict_changed
,
8306 compose_spell_menu_changed
,
8309 alertpanel_error(_("Spell checker could not "
8311 gtkaspell_checkers_strerror());
8312 gtkaspell_checkers_reset_error();
8314 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8318 compose
->gtkaspell
= gtkaspell
;
8319 compose_spell_menu_changed(compose
);
8320 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8323 compose_select_account(compose
, account
, TRUE
);
8325 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8326 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8328 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8329 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8331 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8332 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8334 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8335 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8337 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8338 if (account
->protocol
!= A_NNTP
)
8339 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8340 prefs_common_translated_header_name("To:"));
8342 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8343 prefs_common_translated_header_name("Newsgroups:"));
8345 #ifndef USE_ALT_ADDRBOOK
8346 addressbook_set_target_compose(compose
);
8348 if (mode
!= COMPOSE_REDIRECT
)
8349 compose_set_template_menu(compose
);
8351 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8354 compose_list
= g_list_append(compose_list
, compose
);
8356 if (!prefs_common
.show_ruler
)
8357 gtk_widget_hide(ruler_hbox
);
8359 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8362 compose
->priority
= PRIORITY_NORMAL
;
8363 compose_update_priority_menu_item(compose
);
8365 compose_set_out_encoding(compose
);
8368 compose_update_actions_menu(compose
);
8370 /* Privacy Systems menu */
8371 compose_update_privacy_systems_menu(compose
);
8372 compose_activate_privacy_system(compose
, account
, TRUE
);
8374 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8376 gtk_widget_realize(window
);
8378 gtk_widget_show(window
);
8384 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8389 GtkWidget
*optmenubox
;
8390 GtkWidget
*fromlabel
;
8393 GtkWidget
*from_name
= NULL
;
8395 gint num
= 0, def_menu
= 0;
8397 accounts
= account_get_list();
8398 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8400 optmenubox
= gtk_event_box_new();
8401 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8402 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8404 hbox
= gtk_hbox_new(FALSE
, 4);
8405 from_name
= gtk_entry_new();
8407 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8408 G_CALLBACK(compose_grab_focus_cb
), compose
);
8409 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8410 G_CALLBACK(from_name_activate_cb
), optmenu
);
8412 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8413 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8414 gchar
*name
, *from
= NULL
;
8416 if (ac
== compose
->account
) def_menu
= num
;
8418 name
= g_markup_printf_escaped("<i>%s</i>",
8421 if (ac
== compose
->account
) {
8422 if (ac
->name
&& *ac
->name
) {
8424 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8425 from
= g_strdup_printf("%s <%s>",
8427 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8429 from
= g_strdup_printf("%s",
8431 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8433 if (cur_account
!= compose
->account
) {
8434 gtk_widget_modify_base(
8435 GTK_WIDGET(from_name
),
8436 GTK_STATE_NORMAL
, &default_header_bgcolor
);
8437 gtk_widget_modify_text(
8438 GTK_WIDGET(from_name
),
8439 GTK_STATE_NORMAL
, &default_header_color
);
8442 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8447 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8449 g_signal_connect(G_OBJECT(optmenu
), "changed",
8450 G_CALLBACK(account_activated
),
8452 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8453 G_CALLBACK(compose_entry_popup_extend
),
8456 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8457 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8459 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8460 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8461 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8463 /* Putting only the GtkEntry into focus chain of parent hbox causes
8464 * the account selector combobox next to it to be unreachable when
8465 * navigating widgets in GtkTable with up/down arrow keys.
8466 * Note: gtk_widget_set_can_focus() was not enough. */
8468 l
= g_list_prepend(l
, from_name
);
8469 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), l
);
8472 CLAWS_SET_TIP(optmenubox
,
8473 _("Account to use for this email"));
8474 CLAWS_SET_TIP(from_name
,
8475 _("Sender address to be used"));
8477 compose
->account_combo
= optmenu
;
8478 compose
->from_name
= from_name
;
8483 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8485 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8486 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8487 Compose
*compose
= (Compose
*) data
;
8489 compose
->priority
= value
;
8493 static void compose_reply_change_mode(Compose
*compose
,
8496 gboolean was_modified
= compose
->modified
;
8498 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8500 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8502 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8504 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8506 if (action
== COMPOSE_REPLY_TO_ALL
)
8508 if (action
== COMPOSE_REPLY_TO_SENDER
)
8510 if (action
== COMPOSE_REPLY_TO_LIST
)
8513 compose_remove_header_entries(compose
);
8514 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8515 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8516 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8518 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8519 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8521 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8522 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8523 compose_show_first_last_header(compose
, TRUE
);
8524 compose
->modified
= was_modified
;
8525 compose_set_title(compose
);
8528 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8530 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8531 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8532 Compose
*compose
= (Compose
*) data
;
8535 compose_reply_change_mode(compose
, value
);
8538 static void compose_update_priority_menu_item(Compose
* compose
)
8540 GtkWidget
*menuitem
= NULL
;
8541 switch (compose
->priority
) {
8542 case PRIORITY_HIGHEST
:
8543 menuitem
= gtk_ui_manager_get_widget
8544 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8547 menuitem
= gtk_ui_manager_get_widget
8548 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8550 case PRIORITY_NORMAL
:
8551 menuitem
= gtk_ui_manager_get_widget
8552 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8555 menuitem
= gtk_ui_manager_get_widget
8556 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8558 case PRIORITY_LOWEST
:
8559 menuitem
= gtk_ui_manager_get_widget
8560 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8563 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8566 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8568 Compose
*compose
= (Compose
*) data
;
8570 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8572 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8574 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8577 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8578 g_free(compose
->privacy_system
);
8579 compose
->privacy_system
= NULL
;
8580 g_free(compose
->encdata
);
8581 compose
->encdata
= NULL
;
8582 if (systemid
!= NULL
) {
8583 compose
->privacy_system
= g_strdup(systemid
);
8585 can_sign
= privacy_system_can_sign(systemid
);
8586 can_encrypt
= privacy_system_can_encrypt(systemid
);
8589 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8592 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8593 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8594 gtk_widget_set_sensitive(
8595 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8597 gtk_toggle_tool_button_set_active(
8598 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8599 can_sign
? compose
->use_signing
: FALSE
);
8601 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8602 gtk_widget_set_sensitive(
8603 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8605 gtk_toggle_tool_button_set_active(
8606 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8607 can_encrypt
? compose
->use_encryption
: FALSE
);
8611 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8613 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8614 GtkWidget
*menuitem
= NULL
;
8615 GList
*children
, *amenu
;
8616 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8617 gboolean found
= FALSE
;
8619 if (compose
->privacy_system
!= NULL
) {
8621 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8622 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8623 cm_return_if_fail(menuitem
!= NULL
);
8625 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8628 while (amenu
!= NULL
) {
8629 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8630 if (systemid
!= NULL
) {
8631 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8632 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8633 menuitem
= GTK_WIDGET(amenu
->data
);
8635 can_sign
= privacy_system_can_sign(systemid
);
8636 can_encrypt
= privacy_system_can_encrypt(systemid
);
8640 } else if (strlen(compose
->privacy_system
) == 0 &&
8641 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8642 menuitem
= GTK_WIDGET(amenu
->data
);
8645 can_encrypt
= FALSE
;
8650 amenu
= amenu
->next
;
8652 g_list_free(children
);
8653 if (menuitem
!= NULL
)
8654 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8656 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8657 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8658 "will not be able to sign or encrypt this message."),
8659 compose
->privacy_system
);
8663 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8664 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8665 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8666 gtk_widget_set_sensitive(
8667 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8670 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8671 gtk_widget_set_sensitive(
8672 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8677 static void compose_set_out_encoding(Compose
*compose
)
8679 CharSet out_encoding
;
8680 const gchar
*branch
= NULL
;
8681 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8683 switch(out_encoding
) {
8684 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8685 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8686 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8687 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8688 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8689 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8690 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8691 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8692 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8693 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8694 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8695 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8696 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8697 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8698 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8699 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8700 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8701 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8702 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8703 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8704 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8705 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8706 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8707 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8708 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8709 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8710 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8711 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8712 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8713 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8714 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8715 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8716 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8717 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8719 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8722 static void compose_set_template_menu(Compose
*compose
)
8724 GSList
*tmpl_list
, *cur
;
8728 tmpl_list
= template_get_config();
8730 menu
= gtk_menu_new();
8732 gtk_menu_set_accel_group (GTK_MENU (menu
),
8733 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8734 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8735 Template
*tmpl
= (Template
*)cur
->data
;
8736 gchar
*accel_path
= NULL
;
8737 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8738 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8739 g_signal_connect(G_OBJECT(item
), "activate",
8740 G_CALLBACK(compose_template_activate_cb
),
8742 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8743 gtk_widget_show(item
);
8744 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8745 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8749 gtk_widget_show(menu
);
8750 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8753 void compose_update_actions_menu(Compose
*compose
)
8755 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8758 static void compose_update_privacy_systems_menu(Compose
*compose
)
8760 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8761 GSList
*systems
, *cur
;
8763 GtkWidget
*system_none
;
8765 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8766 GtkWidget
*privacy_menu
= gtk_menu_new();
8768 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8769 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8771 g_signal_connect(G_OBJECT(system_none
), "activate",
8772 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8774 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8775 gtk_widget_show(system_none
);
8777 systems
= privacy_get_system_ids();
8778 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8779 gchar
*systemid
= cur
->data
;
8781 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8782 widget
= gtk_radio_menu_item_new_with_label(group
,
8783 privacy_system_get_name(systemid
));
8784 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8785 g_strdup(systemid
), g_free
);
8786 g_signal_connect(G_OBJECT(widget
), "activate",
8787 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8789 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8790 gtk_widget_show(widget
);
8793 g_slist_free(systems
);
8794 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8795 gtk_widget_show_all(privacy_menu
);
8796 gtk_widget_show_all(privacy_menuitem
);
8799 void compose_reflect_prefs_all(void)
8804 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8805 compose
= (Compose
*)cur
->data
;
8806 compose_set_template_menu(compose
);
8810 void compose_reflect_prefs_pixmap_theme(void)
8815 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8816 compose
= (Compose
*)cur
->data
;
8817 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8821 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8823 const gchar
*qmark
= NULL
;
8825 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8827 switch (compose
->mode
) {
8828 /* use forward-specific quote char */
8829 case COMPOSE_FORWARD
:
8830 case COMPOSE_FORWARD_AS_ATTACH
:
8831 case COMPOSE_FORWARD_INLINE
:
8832 if (compose
->folder
&& compose
->folder
->prefs
&&
8833 compose
->folder
->prefs
->forward_with_format
)
8834 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8835 else if (compose
->account
->forward_with_format
)
8836 qmark
= compose
->account
->forward_quotemark
;
8838 qmark
= prefs_common
.fw_quotemark
;
8841 /* use reply-specific quote char in all other modes */
8843 if (compose
->folder
&& compose
->folder
->prefs
&&
8844 compose
->folder
->prefs
->reply_with_format
)
8845 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8846 else if (compose
->account
->reply_with_format
)
8847 qmark
= compose
->account
->reply_quotemark
;
8849 qmark
= prefs_common
.quotemark
;
8853 if (qmark
== NULL
|| *qmark
== '\0')
8859 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8863 GtkTextBuffer
*buffer
;
8867 gchar
*parsed_str
= NULL
;
8868 gint cursor_pos
= 0;
8869 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8872 /* process the body */
8874 text
= GTK_TEXT_VIEW(compose
->text
);
8875 buffer
= gtk_text_view_get_buffer(text
);
8878 qmark
= compose_quote_char_from_context(compose
);
8880 if (compose
->replyinfo
!= NULL
) {
8883 gtk_text_buffer_set_text(buffer
, "", -1);
8884 mark
= gtk_text_buffer_get_insert(buffer
);
8885 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8887 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8888 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8890 } else if (compose
->fwdinfo
!= NULL
) {
8893 gtk_text_buffer_set_text(buffer
, "", -1);
8894 mark
= gtk_text_buffer_get_insert(buffer
);
8895 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8897 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8898 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8901 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8903 GtkTextIter start
, end
;
8906 gtk_text_buffer_get_start_iter(buffer
, &start
);
8907 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8908 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8910 /* clear the buffer now */
8912 gtk_text_buffer_set_text(buffer
, "", -1);
8914 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8915 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8916 procmsg_msginfo_free( &dummyinfo
);
8922 gtk_text_buffer_set_text(buffer
, "", -1);
8923 mark
= gtk_text_buffer_get_insert(buffer
);
8924 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8927 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8928 compose_insert_sig(compose
, FALSE
);
8930 if (replace
&& parsed_str
) {
8931 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8932 gtk_text_buffer_place_cursor(buffer
, &iter
);
8936 cursor_pos
= quote_fmt_get_cursor_pos();
8937 compose
->set_cursor_pos
= cursor_pos
;
8938 if (cursor_pos
== -1)
8940 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8941 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8942 gtk_text_buffer_place_cursor(buffer
, &iter
);
8945 /* process the other fields */
8947 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8948 compose_template_apply_fields(compose
, tmpl
);
8949 quote_fmt_reset_vartable();
8950 quote_fmtlex_destroy();
8952 compose_changed_cb(NULL
, compose
);
8955 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8956 gtkaspell_highlight_all(compose
->gtkaspell
);
8960 static void compose_template_apply_fields_error(const gchar
*header
)
8965 tr
= g_strdup(C_("'%s' stands for a header name",
8966 "Template '%s' format error."));
8967 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8968 alertpanel_error("%s", text
);
8974 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8976 MsgInfo
* dummyinfo
= NULL
;
8977 MsgInfo
*msginfo
= NULL
;
8980 if (compose
->replyinfo
!= NULL
)
8981 msginfo
= compose
->replyinfo
;
8982 else if (compose
->fwdinfo
!= NULL
)
8983 msginfo
= compose
->fwdinfo
;
8985 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8986 msginfo
= dummyinfo
;
8989 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8991 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8992 compose
->gtkaspell
);
8994 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8996 quote_fmt_scan_string(tmpl
->from
);
8999 buf
= quote_fmt_get_buffer();
9001 compose_template_apply_fields_error("From");
9003 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
9006 quote_fmt_reset_vartable();
9007 quote_fmtlex_destroy();
9010 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
9012 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9013 compose
->gtkaspell
);
9015 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9017 quote_fmt_scan_string(tmpl
->to
);
9020 buf
= quote_fmt_get_buffer();
9022 compose_template_apply_fields_error("To");
9024 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9027 quote_fmt_reset_vartable();
9028 quote_fmtlex_destroy();
9031 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9033 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9034 compose
->gtkaspell
);
9036 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9038 quote_fmt_scan_string(tmpl
->cc
);
9041 buf
= quote_fmt_get_buffer();
9043 compose_template_apply_fields_error("Cc");
9045 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9048 quote_fmt_reset_vartable();
9049 quote_fmtlex_destroy();
9052 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9054 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9055 compose
->gtkaspell
);
9057 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9059 quote_fmt_scan_string(tmpl
->bcc
);
9062 buf
= quote_fmt_get_buffer();
9064 compose_template_apply_fields_error("Bcc");
9066 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9069 quote_fmt_reset_vartable();
9070 quote_fmtlex_destroy();
9073 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9075 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9076 compose
->gtkaspell
);
9078 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9080 quote_fmt_scan_string(tmpl
->replyto
);
9083 buf
= quote_fmt_get_buffer();
9085 compose_template_apply_fields_error("Reply-To");
9087 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9090 quote_fmt_reset_vartable();
9091 quote_fmtlex_destroy();
9094 /* process the subject */
9095 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9097 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9098 compose
->gtkaspell
);
9100 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9102 quote_fmt_scan_string(tmpl
->subject
);
9105 buf
= quote_fmt_get_buffer();
9107 compose_template_apply_fields_error("Subject");
9109 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9112 quote_fmt_reset_vartable();
9113 quote_fmtlex_destroy();
9116 procmsg_msginfo_free( &dummyinfo
);
9119 static void compose_destroy(Compose
*compose
)
9121 GtkAllocation allocation
;
9122 GtkTextBuffer
*buffer
;
9123 GtkClipboard
*clipboard
;
9125 compose_list
= g_list_remove(compose_list
, compose
);
9128 gboolean enable
= TRUE
;
9129 g_slist_foreach(compose
->passworded_ldap_servers
,
9130 _ldap_srv_func
, &enable
);
9131 g_slist_free(compose
->passworded_ldap_servers
);
9134 if (compose
->updating
) {
9135 debug_print("danger, not destroying anything now\n");
9136 compose
->deferred_destroy
= TRUE
;
9140 /* NOTE: address_completion_end() does nothing with the window
9141 * however this may change. */
9142 address_completion_end(compose
->window
);
9144 slist_free_strings_full(compose
->to_list
);
9145 slist_free_strings_full(compose
->newsgroup_list
);
9146 slist_free_strings_full(compose
->header_list
);
9148 slist_free_strings_full(extra_headers
);
9149 extra_headers
= NULL
;
9151 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9153 g_hash_table_destroy(compose
->email_hashtable
);
9155 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9156 compose
->folder_update_callback_id
);
9158 procmsg_msginfo_free(&(compose
->targetinfo
));
9159 procmsg_msginfo_free(&(compose
->replyinfo
));
9160 procmsg_msginfo_free(&(compose
->fwdinfo
));
9162 g_free(compose
->replyto
);
9163 g_free(compose
->cc
);
9164 g_free(compose
->bcc
);
9165 g_free(compose
->newsgroups
);
9166 g_free(compose
->followup_to
);
9168 g_free(compose
->ml_post
);
9170 g_free(compose
->inreplyto
);
9171 g_free(compose
->references
);
9172 g_free(compose
->msgid
);
9173 g_free(compose
->boundary
);
9175 g_free(compose
->redirect_filename
);
9176 if (compose
->undostruct
)
9177 undo_destroy(compose
->undostruct
);
9179 g_free(compose
->sig_str
);
9181 g_free(compose
->exteditor_file
);
9183 g_free(compose
->orig_charset
);
9185 g_free(compose
->privacy_system
);
9186 g_free(compose
->encdata
);
9188 #ifndef USE_ALT_ADDRBOOK
9189 if (addressbook_get_target_compose() == compose
)
9190 addressbook_set_target_compose(NULL
);
9193 if (compose
->gtkaspell
) {
9194 gtkaspell_delete(compose
->gtkaspell
);
9195 compose
->gtkaspell
= NULL
;
9199 if (!compose
->batch
) {
9200 gtk_widget_get_allocation(compose
->window
, &allocation
);
9201 prefs_common
.compose_width
= allocation
.width
;
9202 prefs_common
.compose_height
= allocation
.height
;
9205 if (!gtk_widget_get_parent(compose
->paned
))
9206 gtk_widget_destroy(compose
->paned
);
9207 gtk_widget_destroy(compose
->popupmenu
);
9209 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9210 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9211 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9213 message_search_close(compose
);
9214 gtk_widget_destroy(compose
->window
);
9215 toolbar_destroy(compose
->toolbar
);
9216 g_free(compose
->toolbar
);
9217 cm_mutex_free(compose
->mutex
);
9221 static void compose_attach_info_free(AttachInfo
*ainfo
)
9223 g_free(ainfo
->file
);
9224 g_free(ainfo
->content_type
);
9225 g_free(ainfo
->name
);
9226 g_free(ainfo
->charset
);
9230 static void compose_attach_update_label(Compose
*compose
)
9235 GtkTreeModel
*model
;
9239 if (compose
== NULL
)
9242 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9243 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9244 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9248 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9249 total_size
= ainfo
->size
;
9250 while(gtk_tree_model_iter_next(model
, &iter
)) {
9251 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9252 total_size
+= ainfo
->size
;
9255 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9256 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9260 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9262 Compose
*compose
= (Compose
*)data
;
9263 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9264 GtkTreeSelection
*selection
;
9266 GtkTreeModel
*model
;
9268 selection
= gtk_tree_view_get_selection(tree_view
);
9269 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9270 cm_return_if_fail(sel
);
9272 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9273 GtkTreePath
*path
= cur
->data
;
9274 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9277 gtk_tree_path_free(path
);
9280 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9281 GtkTreeRowReference
*ref
= cur
->data
;
9282 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9285 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9286 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9288 gtk_tree_path_free(path
);
9289 gtk_tree_row_reference_free(ref
);
9293 compose_attach_update_label(compose
);
9296 static struct _AttachProperty
9299 GtkWidget
*mimetype_entry
;
9300 GtkWidget
*encoding_optmenu
;
9301 GtkWidget
*path_entry
;
9302 GtkWidget
*filename_entry
;
9304 GtkWidget
*cancel_btn
;
9307 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9309 gtk_tree_path_free((GtkTreePath
*)ptr
);
9312 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9314 Compose
*compose
= (Compose
*)data
;
9315 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9317 GtkComboBox
*optmenu
;
9318 GtkTreeSelection
*selection
;
9320 GtkTreeModel
*model
;
9323 static gboolean cancelled
;
9325 /* only if one selected */
9326 selection
= gtk_tree_view_get_selection(tree_view
);
9327 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9330 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9331 cm_return_if_fail(sel
);
9333 path
= (GtkTreePath
*) sel
->data
;
9334 gtk_tree_model_get_iter(model
, &iter
, path
);
9335 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9338 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9344 if (!attach_prop
.window
)
9345 compose_attach_property_create(&cancelled
);
9346 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9347 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9348 gtk_widget_show(attach_prop
.window
);
9349 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9350 GTK_WINDOW(compose
->window
));
9352 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9353 if (ainfo
->encoding
== ENC_UNKNOWN
)
9354 combobox_select_by_data(optmenu
, ENC_BASE64
);
9356 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9358 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9359 ainfo
->content_type
? ainfo
->content_type
: "");
9360 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9361 ainfo
->file
? ainfo
->file
: "");
9362 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9363 ainfo
->name
? ainfo
->name
: "");
9366 const gchar
*entry_text
;
9368 gchar
*cnttype
= NULL
;
9375 gtk_widget_hide(attach_prop
.window
);
9376 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9381 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9382 if (*entry_text
!= '\0') {
9385 text
= g_strstrip(g_strdup(entry_text
));
9386 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9387 cnttype
= g_strdup(text
);
9390 alertpanel_error(_("Invalid MIME type."));
9396 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9398 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9399 if (*entry_text
!= '\0') {
9400 if (is_file_exist(entry_text
) &&
9401 (size
= get_file_size(entry_text
)) > 0)
9402 file
= g_strdup(entry_text
);
9405 (_("File doesn't exist or is empty."));
9411 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9412 if (*entry_text
!= '\0') {
9413 g_free(ainfo
->name
);
9414 ainfo
->name
= g_strdup(entry_text
);
9418 g_free(ainfo
->content_type
);
9419 ainfo
->content_type
= cnttype
;
9422 g_free(ainfo
->file
);
9426 ainfo
->size
= (goffset
)size
;
9428 /* update tree store */
9429 text
= to_human_readable(ainfo
->size
);
9430 gtk_tree_model_get_iter(model
, &iter
, path
);
9431 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9432 COL_MIMETYPE
, ainfo
->content_type
,
9434 COL_NAME
, ainfo
->name
,
9435 COL_CHARSET
, ainfo
->charset
,
9441 gtk_tree_path_free(path
);
9444 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9446 label = gtk_label_new(str); \
9447 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9448 GTK_FILL, 0, 0, 0); \
9449 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9451 entry = gtk_entry_new(); \
9452 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9453 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9456 static void compose_attach_property_create(gboolean
*cancelled
)
9462 GtkWidget
*mimetype_entry
;
9465 GtkListStore
*optmenu_menu
;
9466 GtkWidget
*path_entry
;
9467 GtkWidget
*filename_entry
;
9470 GtkWidget
*cancel_btn
;
9471 GList
*mime_type_list
, *strlist
;
9474 debug_print("Creating attach_property window...\n");
9476 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9477 gtk_widget_set_size_request(window
, 480, -1);
9478 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9479 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9480 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9481 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9482 g_signal_connect(G_OBJECT(window
), "delete_event",
9483 G_CALLBACK(attach_property_delete_event
),
9485 g_signal_connect(G_OBJECT(window
), "key_press_event",
9486 G_CALLBACK(attach_property_key_pressed
),
9489 vbox
= gtk_vbox_new(FALSE
, 8);
9490 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9492 table
= gtk_table_new(4, 2, FALSE
);
9493 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9494 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9495 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9497 label
= gtk_label_new(_("MIME type"));
9498 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9500 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9501 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9502 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9503 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9505 /* stuff with list */
9506 mime_type_list
= procmime_get_mime_type_list();
9508 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9509 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9512 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9514 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9517 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9518 (GCompareFunc
)g_strcmp0
);
9521 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9522 mime_type_list
= mime_type_list
->next
) {
9523 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9524 g_free(mime_type_list
->data
);
9526 g_list_free(strlist
);
9527 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9528 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9530 label
= gtk_label_new(_("Encoding"));
9531 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9533 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9535 hbox
= gtk_hbox_new(FALSE
, 0);
9536 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9537 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9539 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9540 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9542 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9543 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9544 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9545 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9546 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9548 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9550 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9551 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9553 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9554 &ok_btn
, GTK_STOCK_OK
,
9556 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9557 gtk_widget_grab_default(ok_btn
);
9559 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9560 G_CALLBACK(attach_property_ok
),
9562 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9563 G_CALLBACK(attach_property_cancel
),
9566 gtk_widget_show_all(vbox
);
9568 attach_prop
.window
= window
;
9569 attach_prop
.mimetype_entry
= mimetype_entry
;
9570 attach_prop
.encoding_optmenu
= optmenu
;
9571 attach_prop
.path_entry
= path_entry
;
9572 attach_prop
.filename_entry
= filename_entry
;
9573 attach_prop
.ok_btn
= ok_btn
;
9574 attach_prop
.cancel_btn
= cancel_btn
;
9577 #undef SET_LABEL_AND_ENTRY
9579 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9585 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9591 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9592 gboolean
*cancelled
)
9600 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9602 gboolean
*cancelled
)
9604 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9608 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9616 static gboolean
compose_can_autosave(Compose
*compose
)
9618 if (compose
->privacy_system
&& compose
->use_encryption
)
9619 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9621 return prefs_common
.autosave
;
9625 * compose_exec_ext_editor:
9627 * Open (and optionally embed) external editor
9629 static void compose_exec_ext_editor(Compose
*compose
)
9634 GdkNativeWindow socket_wid
= 0;
9635 #endif /* G_OS_WIN32 */
9637 GError
*error
= NULL
;
9642 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9643 G_DIR_SEPARATOR
, compose
);
9645 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9646 alertpanel_error(_("Could not write the body to file:\n%s"),
9652 if (compose_get_ext_editor_uses_socket()) {
9654 /* Only allow one socket */
9655 if (compose
->exteditor_socket
!= NULL
) {
9656 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9657 /* Move the focus off of the socket */
9658 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9663 /* Create the receiving GtkSocket */
9664 socket
= gtk_socket_new ();
9665 g_signal_connect (GTK_OBJECT(socket
), "plug-removed",
9666 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9668 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9669 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9670 /* Realize the socket so that we can use its ID */
9671 gtk_widget_realize(socket
);
9672 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9673 compose
->exteditor_socket
= socket
;
9674 #endif /* G_OS_WIN32 */
9677 if (compose_get_ext_editor_cmd_valid()) {
9678 if (compose_get_ext_editor_uses_socket()) {
9680 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9681 s
= strstr(p
, "%w");
9683 if (strstr(p
, "%s") < s
)
9684 cmd
= g_strdup_printf(p
, tmp
, socket_wid
);
9686 cmd
= g_strdup_printf(p
, socket_wid
, tmp
);
9688 #endif /* G_OS_WIN32 */
9690 cmd
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), tmp
);
9693 if (prefs_common_get_ext_editor_cmd())
9694 g_warning("External editor command-line is invalid: '%s'",
9695 prefs_common_get_ext_editor_cmd());
9696 cmd
= g_strdup_printf(DEFAULT_EDITOR_CMD
, tmp
);
9699 argv
= strsplit_with_quote(cmd
, " ", 0);
9701 if (!g_spawn_async(NULL
, argv
, NULL
,
9702 G_SPAWN_DO_NOT_REAP_CHILD
| G_SPAWN_SEARCH_PATH
,
9703 NULL
, NULL
, &pid
, &error
)) {
9704 alertpanel_error(_("Could not spawn the following "
9705 "command:\n%s\n%s"),
9706 cmd
, error
? error
->message
: _("Unknown error"));
9708 g_error_free(error
);
9717 compose
->exteditor_file
= g_strdup(tmp
);
9718 compose
->exteditor_pid
= pid
;
9719 compose
->exteditor_tag
= g_child_watch_add(pid
,
9720 compose_ext_editor_closed_cb
,
9723 compose_set_ext_editor_sensitive(compose
, FALSE
);
9729 * compose_ext_editor_cb:
9731 * External editor has closed (called by g_child_watch)
9733 static void compose_ext_editor_closed_cb(GPid pid
, gint exit_status
, gpointer data
)
9735 Compose
*compose
= (Compose
*)data
;
9736 GError
*error
= NULL
;
9737 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9738 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9739 GtkTextIter start
, end
;
9742 if (!g_spawn_check_exit_status(exit_status
, &error
)) {
9744 _("External editor stopped with an error: %s"),
9745 error
? error
->message
: _("Unknown error"));
9747 g_error_free(error
);
9749 g_spawn_close_pid(compose
->exteditor_pid
);
9751 gtk_text_buffer_set_text(buffer
, "", -1);
9752 compose_insert_file(compose
, compose
->exteditor_file
);
9753 compose_changed_cb(NULL
, compose
);
9755 /* Check if we should save the draft or not */
9756 if (compose_can_autosave(compose
))
9757 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9759 if (claws_unlink(compose
->exteditor_file
) < 0)
9760 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9762 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9763 gtk_text_buffer_get_start_iter(buffer
, &start
);
9764 gtk_text_buffer_get_end_iter(buffer
, &end
);
9765 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9766 if (chars
&& strlen(chars
) > 0)
9767 compose
->modified
= TRUE
;
9770 compose_set_ext_editor_sensitive(compose
, TRUE
);
9772 g_free(compose
->exteditor_file
);
9773 compose
->exteditor_file
= NULL
;
9774 compose
->exteditor_pid
= -1;
9775 compose
->exteditor_tag
= -1;
9776 if (compose
->exteditor_socket
) {
9777 gtk_widget_destroy(compose
->exteditor_socket
);
9778 compose
->exteditor_socket
= NULL
;
9783 static gboolean
compose_get_ext_editor_cmd_valid()
9785 gboolean has_s
= FALSE
;
9786 gboolean has_w
= FALSE
;
9787 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9790 while ((p
= strchr(p
, '%'))) {
9796 } else if (*p
== 'w') {
9807 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9809 GPid pid
= compose
->exteditor_pid
;
9816 msg
= g_strdup_printf
9817 (_("The external editor is still working.\n"
9818 "Force terminating the process?\n"
9819 "process id: %d"), pid
);
9820 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
,
9821 GTK_STOCK_YES
, NULL
, ALERTFOCUS_FIRST
,
9822 FALSE
, NULL
, ALERT_WARNING
);
9825 if (val
== G_ALERTALTERNATE
) {
9826 g_source_remove(compose
->exteditor_tag
);
9829 if (!TerminateProcess(compose
->exteditor_pid
, 0))
9830 perror("TerminateProcess");
9832 if (kill(pid
, SIGTERM
) < 0) perror("kill");
9833 waitpid(compose
->exteditor_pid
, NULL
, 0);
9834 #endif /* G_OS_WIN32 */
9836 g_warning("Terminated process id: %d. "
9837 "Temporary file: %s", pid
, compose
->exteditor_file
);
9838 g_spawn_close_pid(compose
->exteditor_pid
);
9840 compose_set_ext_editor_sensitive(compose
, TRUE
);
9842 g_free(compose
->exteditor_file
);
9843 compose
->exteditor_file
= NULL
;
9844 compose
->exteditor_pid
= -1;
9845 compose
->exteditor_tag
= -1;
9853 static char *ext_editor_menu_entries
[] = {
9854 "Menu/Message/Send",
9855 "Menu/Message/SendLater",
9856 "Menu/Message/InsertFile",
9857 "Menu/Message/InsertSig",
9858 "Menu/Message/ReplaceSig",
9859 "Menu/Message/Save",
9860 "Menu/Message/Print",
9865 "Menu/Tools/ShowRuler",
9866 "Menu/Tools/Actions",
9871 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9876 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9877 cm_menu_set_sensitive_full(compose
->ui_manager
,
9878 ext_editor_menu_entries
[i
], sensitive
);
9881 if (compose_get_ext_editor_uses_socket()) {
9883 if (compose
->exteditor_socket
)
9884 gtk_widget_hide(compose
->exteditor_socket
);
9885 gtk_widget_show(compose
->scrolledwin
);
9886 if (prefs_common
.show_ruler
)
9887 gtk_widget_show(compose
->ruler_hbox
);
9888 /* Fix the focus, as it doesn't go anywhere when the
9889 * socket is hidden or destroyed */
9890 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9892 g_assert (compose
->exteditor_socket
!= NULL
);
9893 /* Fix the focus, as it doesn't go anywhere when the
9894 * edit box is hidden */
9895 if (gtk_widget_is_focus(compose
->text
))
9896 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9897 gtk_widget_hide(compose
->scrolledwin
);
9898 gtk_widget_hide(compose
->ruler_hbox
);
9899 gtk_widget_show(compose
->exteditor_socket
);
9902 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9904 if (compose
->toolbar
->send_btn
)
9905 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9906 if (compose
->toolbar
->sendl_btn
)
9907 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9908 if (compose
->toolbar
->draft_btn
)
9909 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9910 if (compose
->toolbar
->insert_btn
)
9911 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9912 if (compose
->toolbar
->sig_btn
)
9913 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9914 if (compose
->toolbar
->exteditor_btn
)
9915 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9916 if (compose
->toolbar
->linewrap_current_btn
)
9917 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9918 if (compose
->toolbar
->linewrap_all_btn
)
9919 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9922 static gboolean
compose_get_ext_editor_uses_socket()
9924 return (prefs_common_get_ext_editor_cmd() &&
9925 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9929 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9931 compose
->exteditor_socket
= NULL
;
9932 /* returning FALSE allows destruction of the socket */
9935 #endif /* G_OS_WIN32 */
9938 * compose_undo_state_changed:
9940 * Change the sensivity of the menuentries undo and redo
9942 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9943 gint redo_state
, gpointer data
)
9945 Compose
*compose
= (Compose
*)data
;
9947 switch (undo_state
) {
9948 case UNDO_STATE_TRUE
:
9949 if (!undostruct
->undo_state
) {
9950 undostruct
->undo_state
= TRUE
;
9951 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9954 case UNDO_STATE_FALSE
:
9955 if (undostruct
->undo_state
) {
9956 undostruct
->undo_state
= FALSE
;
9957 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9960 case UNDO_STATE_UNCHANGED
:
9962 case UNDO_STATE_REFRESH
:
9963 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9966 g_warning("Undo state not recognized");
9970 switch (redo_state
) {
9971 case UNDO_STATE_TRUE
:
9972 if (!undostruct
->redo_state
) {
9973 undostruct
->redo_state
= TRUE
;
9974 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9977 case UNDO_STATE_FALSE
:
9978 if (undostruct
->redo_state
) {
9979 undostruct
->redo_state
= FALSE
;
9980 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
9983 case UNDO_STATE_UNCHANGED
:
9985 case UNDO_STATE_REFRESH
:
9986 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
9989 g_warning("Redo state not recognized");
9994 /* callback functions */
9996 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
9997 GtkAllocation
*allocation
,
10000 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10003 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10004 * includes "non-client" (windows-izm) in calculation, so this calculation
10005 * may not be accurate.
10007 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10008 GtkAllocation
*allocation
,
10009 GtkSHRuler
*shruler
)
10011 if (prefs_common
.show_ruler
) {
10012 gint char_width
= 0, char_height
= 0;
10013 gint line_width_in_chars
;
10015 gtkut_get_font_size(GTK_WIDGET(widget
),
10016 &char_width
, &char_height
);
10017 line_width_in_chars
=
10018 (allocation
->width
- allocation
->x
) / char_width
;
10020 /* got the maximum */
10021 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10022 0.0, line_width_in_chars
, 0);
10031 ComposePrefType type
;
10032 gboolean entry_marked
;
10033 } HeaderEntryState
;
10035 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10037 Compose
*compose
= (Compose
*)data
;
10040 gchar
*folderidentifier
;
10041 gint account_id
= 0;
10042 GtkTreeModel
*menu
;
10044 GSList
*list
, *saved_list
= NULL
;
10045 HeaderEntryState
*state
;
10047 /* Get ID of active account in the combo box */
10048 menu
= gtk_combo_box_get_model(optmenu
);
10049 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10050 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10052 ac
= account_find_from_id(account_id
);
10053 cm_return_if_fail(ac
!= NULL
);
10055 if (ac
!= compose
->account
) {
10056 compose_select_account(compose
, ac
, FALSE
);
10058 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10059 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10061 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10062 compose_destroy_headerentry(compose
, hentry
);
10065 state
= g_malloc0(sizeof(HeaderEntryState
));
10066 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10067 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10068 state
->entry
= gtk_editable_get_chars(
10069 GTK_EDITABLE(hentry
->entry
), 0, -1);
10070 state
->type
= hentry
->type
;
10072 saved_list
= g_slist_append(saved_list
, state
);
10073 compose_destroy_headerentry(compose
, hentry
);
10076 compose
->header_last
= NULL
;
10077 g_slist_free(compose
->header_list
);
10078 compose
->header_list
= NULL
;
10079 compose
->header_nextrow
= 1;
10080 compose_create_header_entry(compose
);
10082 if (ac
->set_autocc
&& ac
->auto_cc
)
10083 compose_entry_append(compose
, ac
->auto_cc
,
10084 COMPOSE_CC
, PREF_ACCOUNT
);
10085 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10086 compose_entry_append(compose
, ac
->auto_bcc
,
10087 COMPOSE_BCC
, PREF_ACCOUNT
);
10088 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10089 compose_entry_append(compose
, ac
->auto_replyto
,
10090 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10092 for (list
= saved_list
; list
; list
= list
->next
) {
10093 state
= (HeaderEntryState
*) list
->data
;
10095 compose_add_header_entry(compose
, state
->header
,
10096 state
->entry
, state
->type
);
10098 g_free(state
->header
);
10099 g_free(state
->entry
);
10102 g_slist_free(saved_list
);
10104 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10105 (ac
->protocol
== A_NNTP
) ?
10106 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10109 /* Set message save folder */
10110 compose_set_save_to(compose
, NULL
);
10111 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10112 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10113 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10114 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10115 compose_set_save_to(compose
, folderidentifier
);
10116 g_free(folderidentifier
);
10117 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10118 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10119 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10121 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10122 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10123 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10124 (compose
->account
, F_OUTBOX
));
10125 compose_set_save_to(compose
, folderidentifier
);
10126 g_free(folderidentifier
);
10130 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10131 GtkTreeViewColumn
*column
, Compose
*compose
)
10133 compose_attach_property(NULL
, compose
);
10136 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10139 Compose
*compose
= (Compose
*)data
;
10140 GtkTreeSelection
*attach_selection
;
10141 gint attach_nr_selected
;
10144 if (!event
) return FALSE
;
10146 if (event
->button
== 3) {
10147 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10148 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10150 /* If no rows, or just one row is selected, right-click should
10151 * open menu relevant to the row being right-clicked on. We
10152 * achieve that by selecting the clicked row first. If more
10153 * than one row is selected, we shouldn't modify the selection,
10154 * as user may want to remove selected rows (attachments). */
10155 if (attach_nr_selected
< 2) {
10156 gtk_tree_selection_unselect_all(attach_selection
);
10157 attach_nr_selected
= 0;
10158 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10159 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10160 if (path
!= NULL
) {
10161 gtk_tree_selection_select_path(attach_selection
, path
);
10162 gtk_tree_path_free(path
);
10163 attach_nr_selected
++;
10167 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10168 /* Properties menu item makes no sense with more than one row
10169 * selected, the properties dialog can only edit one attachment. */
10170 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10172 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
10173 NULL
, NULL
, event
->button
, event
->time
);
10180 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10183 Compose
*compose
= (Compose
*)data
;
10185 if (!event
) return FALSE
;
10187 switch (event
->keyval
) {
10188 case GDK_KEY_Delete
:
10189 compose_attach_remove_selected(NULL
, compose
);
10195 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10197 toolbar_comp_set_sensitive(compose
, allow
);
10198 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10199 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10201 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10203 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10204 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10205 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10207 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10211 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10213 Compose
*compose
= (Compose
*)data
;
10216 if (compose
->exteditor_tag
!= -1) {
10217 debug_print("ignoring send: external editor still open\n");
10221 if (prefs_common
.work_offline
&&
10222 !inc_offline_should_override(TRUE
,
10223 _("Claws Mail needs network access in order "
10224 "to send this email.")))
10227 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10228 g_source_remove(compose
->draft_timeout_tag
);
10229 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10232 compose_send(compose
);
10235 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10237 Compose
*compose
= (Compose
*)data
;
10238 ComposeQueueResult val
;
10241 compose_allow_user_actions(compose
, FALSE
);
10242 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10243 compose_allow_user_actions(compose
, TRUE
);
10246 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10247 compose_close(compose
);
10249 _display_queue_error(val
);
10252 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10255 #define DRAFTED_AT_EXIT "drafted_at_exit"
10256 static void compose_register_draft(MsgInfo
*info
)
10258 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10259 DRAFTED_AT_EXIT
, NULL
);
10260 FILE *fp
= claws_fopen(filepath
, "ab");
10263 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
10271 gboolean
compose_draft (gpointer data
, guint action
)
10273 Compose
*compose
= (Compose
*)data
;
10275 FolderItemPrefs
*prefs
;
10279 MsgFlags flag
= {0, 0};
10280 static gboolean lock
= FALSE
;
10281 MsgInfo
*newmsginfo
;
10283 gboolean target_locked
= FALSE
;
10284 gboolean err
= FALSE
;
10287 if (lock
) return FALSE
;
10289 if (compose
->sending
)
10292 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10293 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10295 if (!g_mutex_trylock(compose
->mutex
)) {
10296 /* we don't want to lock the mutex once it's available,
10297 * because as the only other part of compose.c locking
10298 * it is compose_close - which means once unlocked,
10299 * the compose struct will be freed */
10300 debug_print("couldn't lock mutex, probably sending\n");
10306 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10307 G_DIR_SEPARATOR
, compose
);
10308 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10309 FILE_OP_ERROR(tmp
, "claws_fopen");
10313 /* chmod for security unless folder chmod is set */
10314 prefs
= draft
->prefs
;
10315 if (prefs
&& prefs
->enable_folder_chmod
&& prefs
->folder_chmod
) {
10316 filemode
= prefs
->folder_chmod
;
10317 if (filemode
& S_IRGRP
) filemode
|= S_IWGRP
;
10318 if (filemode
& S_IROTH
) filemode
|= S_IWOTH
;
10319 if (chmod(tmp
, filemode
) < 0)
10320 FILE_OP_ERROR(tmp
, "chmod");
10321 } else if (change_file_mode_rw(fp
, tmp
) < 0) {
10322 FILE_OP_ERROR(tmp
, "chmod");
10323 g_warning("can't change file mode");
10326 /* Save draft infos */
10327 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10328 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10330 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10331 gchar
*savefolderid
;
10333 savefolderid
= compose_get_save_to(compose
);
10334 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10335 g_free(savefolderid
);
10337 if (compose
->return_receipt
) {
10338 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10340 if (compose
->privacy_system
) {
10341 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10342 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10343 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10346 /* Message-ID of message replying to */
10347 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10348 gchar
*folderid
= NULL
;
10350 if (compose
->replyinfo
->folder
)
10351 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10352 if (folderid
== NULL
)
10353 folderid
= g_strdup("NULL");
10355 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10358 /* Message-ID of message forwarding to */
10359 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10360 gchar
*folderid
= NULL
;
10362 if (compose
->fwdinfo
->folder
)
10363 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10364 if (folderid
== NULL
)
10365 folderid
= g_strdup("NULL");
10367 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10371 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10372 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10374 sheaders
= compose_get_manual_headers_info(compose
);
10375 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10378 /* end of headers */
10379 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10386 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10390 if (claws_safe_fclose(fp
) == EOF
) {
10394 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10395 if (compose
->targetinfo
) {
10396 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10398 flag
.perm_flags
|= MSG_LOCKED
;
10400 flag
.tmp_flags
= MSG_DRAFT
;
10402 folder_item_scan(draft
);
10403 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10404 MsgInfo
*tmpinfo
= NULL
;
10405 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10406 if (compose
->msgid
) {
10407 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10410 msgnum
= tmpinfo
->msgnum
;
10411 procmsg_msginfo_free(&tmpinfo
);
10412 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10414 debug_print("didn't get draft msgnum after scanning\n");
10417 debug_print("got draft msgnum %d from adding\n", msgnum
);
10423 if (action
!= COMPOSE_AUTO_SAVE
) {
10424 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10425 alertpanel_error(_("Could not save draft."));
10428 gtkut_window_popup(compose
->window
);
10429 val
= alertpanel_full(_("Could not save draft"),
10430 _("Could not save draft.\n"
10431 "Do you want to cancel exit or discard this email?"),
10432 _("_Cancel exit"), _("_Discard email"), NULL
, ALERTFOCUS_FIRST
,
10433 FALSE
, NULL
, ALERT_QUESTION
);
10434 if (val
== G_ALERTALTERNATE
) {
10436 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10437 compose_close(compose
);
10441 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10450 if (compose
->mode
== COMPOSE_REEDIT
) {
10451 compose_remove_reedit_target(compose
, TRUE
);
10454 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10457 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10459 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10461 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10462 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10463 procmsg_msginfo_set_flags(newmsginfo
, 0,
10464 MSG_HAS_ATTACHMENT
);
10466 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10467 compose_register_draft(newmsginfo
);
10469 procmsg_msginfo_free(&newmsginfo
);
10472 folder_item_scan(draft
);
10474 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10476 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10477 compose_close(compose
);
10484 GError
*error
= NULL
;
10489 goffset size
, mtime
;
10491 path
= folder_item_fetch_msg(draft
, msgnum
);
10492 if (path
== NULL
) {
10493 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10497 f
= g_file_new_for_path(path
);
10498 fi
= g_file_query_info(f
, "standard::size,time::modified",
10499 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10500 if (error
!= NULL
) {
10501 debug_print("couldn't query file info for '%s': %s\n",
10502 path
, error
->message
);
10503 g_error_free(error
);
10508 size
= g_file_info_get_size(fi
);
10509 g_file_info_get_modification_time(fi
, &tv
);
10511 g_object_unref(fi
);
10514 if (g_stat(path
, &s
) < 0) {
10515 FILE_OP_ERROR(path
, "stat");
10520 mtime
= s
.st_mtime
;
10524 procmsg_msginfo_free(&(compose
->targetinfo
));
10525 compose
->targetinfo
= procmsg_msginfo_new();
10526 compose
->targetinfo
->msgnum
= msgnum
;
10527 compose
->targetinfo
->size
= size
;
10528 compose
->targetinfo
->mtime
= mtime
;
10529 compose
->targetinfo
->folder
= draft
;
10531 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10532 compose
->mode
= COMPOSE_REEDIT
;
10534 if (action
== COMPOSE_AUTO_SAVE
) {
10535 compose
->autosaved_draft
= compose
->targetinfo
;
10537 compose
->modified
= FALSE
;
10538 compose_set_title(compose
);
10542 g_mutex_unlock(compose
->mutex
);
10546 void compose_clear_exit_drafts(void)
10548 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10549 DRAFTED_AT_EXIT
, NULL
);
10550 if (is_file_exist(filepath
))
10551 claws_unlink(filepath
);
10556 void compose_reopen_exit_drafts(void)
10558 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10559 DRAFTED_AT_EXIT
, NULL
);
10560 FILE *fp
= claws_fopen(filepath
, "rb");
10564 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10565 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10566 const gchar
*folder
= parts
[0];
10567 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10569 if (folder
&& *folder
&& msgnum
> -1) {
10570 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10571 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10573 compose_reedit(info
, FALSE
);
10580 compose_clear_exit_drafts();
10583 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10585 Compose
*compose
= (Compose
*)data
;
10586 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10587 compose
->rmode
= COMPOSE_REEDIT
;
10590 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10592 if (compose
&& file_list
) {
10595 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10596 gchar
*file
= (gchar
*) tmp
->data
;
10597 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10598 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10599 compose_changed_cb(NULL
, compose
);
10604 g_free(utf8_filename
);
10609 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10611 Compose
*compose
= (Compose
*)data
;
10614 if (compose
->redirect_filename
!= NULL
)
10617 /* Set focus_window properly, in case we were called via popup menu,
10618 * which unsets it (via focus_out_event callback on compose window). */
10619 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10621 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10624 compose_attach_from_list(compose
, file_list
, TRUE
);
10625 g_list_free(file_list
);
10629 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10631 Compose
*compose
= (Compose
*)data
;
10633 gint files_inserted
= 0;
10635 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10640 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10641 gchar
*file
= (gchar
*) tmp
->data
;
10642 gchar
*filedup
= g_strdup(file
);
10643 gchar
*shortfile
= g_path_get_basename(filedup
);
10644 ComposeInsertResult res
;
10645 /* insert the file if the file is short or if the user confirmed that
10646 he/she wants to insert the large file */
10647 res
= compose_insert_file(compose
, file
);
10648 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10649 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10650 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10651 alertpanel_error(_("File '%s' contained invalid characters\n"
10652 "for the current encoding, insertion may be incorrect."),
10654 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10661 g_list_free(file_list
);
10665 if (files_inserted
> 0 && compose
->gtkaspell
&&
10666 compose
->gtkaspell
->check_while_typing
)
10667 gtkaspell_highlight_all(compose
->gtkaspell
);
10671 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10673 Compose
*compose
= (Compose
*)data
;
10675 compose_insert_sig(compose
, FALSE
);
10678 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10680 Compose
*compose
= (Compose
*)data
;
10682 compose_insert_sig(compose
, TRUE
);
10685 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10689 Compose
*compose
= (Compose
*)data
;
10691 gtkut_widget_get_uposition(widget
, &x
, &y
);
10692 if (!compose
->batch
) {
10693 prefs_common
.compose_x
= x
;
10694 prefs_common
.compose_y
= y
;
10696 if (compose
->sending
|| compose
->updating
)
10698 compose_close_cb(NULL
, compose
);
10702 void compose_close_toolbar(Compose
*compose
)
10704 compose_close_cb(NULL
, compose
);
10707 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10709 Compose
*compose
= (Compose
*)data
;
10712 if (compose
->exteditor_tag
!= -1) {
10713 if (!compose_ext_editor_kill(compose
))
10717 if (compose
->modified
) {
10718 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10719 if (!g_mutex_trylock(compose
->mutex
)) {
10720 /* we don't want to lock the mutex once it's available,
10721 * because as the only other part of compose.c locking
10722 * it is compose_close - which means once unlocked,
10723 * the compose struct will be freed */
10724 debug_print("couldn't lock mutex, probably sending\n");
10727 if (!reedit
|| compose
->folder
->stype
== F_DRAFT
) {
10728 val
= alertpanel(_("Discard message"),
10729 _("This message has been modified. Discard it?"),
10730 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10733 val
= alertpanel(_("Save changes"),
10734 _("This message has been modified. Save the latest changes?"),
10735 _("_Don't save"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10736 ALERTFOCUS_SECOND
);
10738 g_mutex_unlock(compose
->mutex
);
10740 case G_ALERTDEFAULT
:
10741 if (compose_can_autosave(compose
) && !reedit
)
10742 compose_remove_draft(compose
);
10744 case G_ALERTALTERNATE
:
10745 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10752 compose_close(compose
);
10755 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10757 Compose
*compose
= (Compose
*) data
;
10759 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10760 if (compose
->targetinfo
)
10761 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10764 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10766 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10767 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10768 Compose
*compose
= (Compose
*) data
;
10771 compose
->out_encoding
= (CharSet
)value
;
10774 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10776 Compose
*compose
= (Compose
*)data
;
10778 #ifndef USE_ALT_ADDRBOOK
10779 addressbook_open(compose
);
10781 GError
* error
= NULL
;
10782 addressbook_connect_signals(compose
);
10783 addressbook_dbus_open(TRUE
, &error
);
10785 g_warning("%s", error
->message
);
10786 g_error_free(error
);
10791 static void about_show_cb(GtkAction
*action
, gpointer data
)
10796 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10798 Compose
*compose
= (Compose
*)data
;
10803 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10804 cm_return_if_fail(tmpl
!= NULL
);
10806 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10808 val
= alertpanel(_("Apply template"), msg
,
10809 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
, ALERTFOCUS_FIRST
);
10812 if (val
== G_ALERTDEFAULT
)
10813 compose_template_apply(compose
, tmpl
, TRUE
);
10814 else if (val
== G_ALERTALTERNATE
)
10815 compose_template_apply(compose
, tmpl
, FALSE
);
10818 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10820 Compose
*compose
= (Compose
*)data
;
10822 if (compose
->exteditor_tag
!= -1) {
10823 debug_print("ignoring open external editor: external editor still open\n");
10826 compose_exec_ext_editor(compose
);
10829 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10831 Compose
*compose
= (Compose
*)data
;
10832 gboolean prev_autowrap
= compose
->autowrap
;
10834 compose
->autowrap
= FALSE
;
10835 undo_undo(compose
->undostruct
);
10836 compose
->autowrap
= prev_autowrap
;
10839 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10841 Compose
*compose
= (Compose
*)data
;
10842 gboolean prev_autowrap
= compose
->autowrap
;
10844 compose
->autowrap
= FALSE
;
10845 undo_redo(compose
->undostruct
);
10846 compose
->autowrap
= prev_autowrap
;
10849 static void entry_cut_clipboard(GtkWidget
*entry
)
10851 if (GTK_IS_EDITABLE(entry
))
10852 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10853 else if (GTK_IS_TEXT_VIEW(entry
))
10854 gtk_text_buffer_cut_clipboard(
10855 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10856 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10860 static void entry_copy_clipboard(GtkWidget
*entry
)
10862 if (GTK_IS_EDITABLE(entry
))
10863 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10864 else if (GTK_IS_TEXT_VIEW(entry
))
10865 gtk_text_buffer_copy_clipboard(
10866 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10867 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10870 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10871 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10873 if (GTK_IS_TEXT_VIEW(entry
)) {
10874 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10875 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10876 GtkTextIter start_iter
, end_iter
;
10878 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10880 if (contents
== NULL
)
10883 /* we shouldn't delete the selection when middle-click-pasting, or we
10884 * can't mid-click-paste our own selection */
10885 if (clip
!= GDK_SELECTION_PRIMARY
) {
10886 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10887 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10890 if (insert_place
== NULL
) {
10891 /* if insert_place isn't specified, insert at the cursor.
10892 * used for Ctrl-V pasting */
10893 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10894 start
= gtk_text_iter_get_offset(&start_iter
);
10895 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10897 /* if insert_place is specified, paste here.
10898 * used for mid-click-pasting */
10899 start
= gtk_text_iter_get_offset(insert_place
);
10900 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10901 if (prefs_common
.primary_paste_unselects
)
10902 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10906 /* paste unwrapped: mark the paste so it's not wrapped later */
10907 end
= start
+ strlen(contents
);
10908 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10909 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10910 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10911 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10912 /* rewrap paragraph now (after a mid-click-paste) */
10913 mark_start
= gtk_text_buffer_get_insert(buffer
);
10914 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10915 gtk_text_iter_backward_char(&start_iter
);
10916 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10918 } else if (GTK_IS_EDITABLE(entry
))
10919 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10921 compose
->modified
= TRUE
;
10924 static void entry_allsel(GtkWidget
*entry
)
10926 if (GTK_IS_EDITABLE(entry
))
10927 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10928 else if (GTK_IS_TEXT_VIEW(entry
)) {
10929 GtkTextIter startiter
, enditer
;
10930 GtkTextBuffer
*textbuf
;
10932 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10933 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10934 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10936 gtk_text_buffer_move_mark_by_name(textbuf
,
10937 "selection_bound", &startiter
);
10938 gtk_text_buffer_move_mark_by_name(textbuf
,
10939 "insert", &enditer
);
10943 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10945 Compose
*compose
= (Compose
*)data
;
10946 if (compose
->focused_editable
10947 #ifndef GENERIC_UMPC
10948 && gtk_widget_has_focus(compose
->focused_editable
)
10951 entry_cut_clipboard(compose
->focused_editable
);
10954 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10956 Compose
*compose
= (Compose
*)data
;
10957 if (compose
->focused_editable
10958 #ifndef GENERIC_UMPC
10959 && gtk_widget_has_focus(compose
->focused_editable
)
10962 entry_copy_clipboard(compose
->focused_editable
);
10965 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10967 Compose
*compose
= (Compose
*)data
;
10968 gint prev_autowrap
;
10969 GtkTextBuffer
*buffer
;
10971 if (compose
->focused_editable
10972 #ifndef GENERIC_UMPC
10973 && gtk_widget_has_focus(compose
->focused_editable
)
10976 entry_paste_clipboard(compose
, compose
->focused_editable
,
10977 prefs_common
.linewrap_pastes
,
10978 GDK_SELECTION_CLIPBOARD
, NULL
);
10983 #ifndef GENERIC_UMPC
10984 gtk_widget_has_focus(compose
->text
) &&
10986 compose
->gtkaspell
&&
10987 compose
->gtkaspell
->check_while_typing
)
10988 gtkaspell_highlight_all(compose
->gtkaspell
);
10992 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
10994 Compose
*compose
= (Compose
*)data
;
10995 gint wrap_quote
= prefs_common
.linewrap_quote
;
10996 if (compose
->focused_editable
10997 #ifndef GENERIC_UMPC
10998 && gtk_widget_has_focus(compose
->focused_editable
)
11001 /* let text_insert() (called directly or at a later time
11002 * after the gtk_editable_paste_clipboard) know that
11003 * text is to be inserted as a quotation. implemented
11004 * by using a simple refcount... */
11005 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11006 G_OBJECT(compose
->focused_editable
),
11007 "paste_as_quotation"));
11008 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11009 "paste_as_quotation",
11010 GINT_TO_POINTER(paste_as_quotation
+ 1));
11011 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11012 entry_paste_clipboard(compose
, compose
->focused_editable
,
11013 prefs_common
.linewrap_pastes
,
11014 GDK_SELECTION_CLIPBOARD
, NULL
);
11015 prefs_common
.linewrap_quote
= wrap_quote
;
11019 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11021 Compose
*compose
= (Compose
*)data
;
11022 gint prev_autowrap
;
11023 GtkTextBuffer
*buffer
;
11025 if (compose
->focused_editable
11026 #ifndef GENERIC_UMPC
11027 && gtk_widget_has_focus(compose
->focused_editable
)
11030 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11031 GDK_SELECTION_CLIPBOARD
, NULL
);
11036 #ifndef GENERIC_UMPC
11037 gtk_widget_has_focus(compose
->text
) &&
11039 compose
->gtkaspell
&&
11040 compose
->gtkaspell
->check_while_typing
)
11041 gtkaspell_highlight_all(compose
->gtkaspell
);
11045 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11047 Compose
*compose
= (Compose
*)data
;
11048 gint prev_autowrap
;
11049 GtkTextBuffer
*buffer
;
11051 if (compose
->focused_editable
11052 #ifndef GENERIC_UMPC
11053 && gtk_widget_has_focus(compose
->focused_editable
)
11056 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11057 GDK_SELECTION_CLIPBOARD
, NULL
);
11062 #ifndef GENERIC_UMPC
11063 gtk_widget_has_focus(compose
->text
) &&
11065 compose
->gtkaspell
&&
11066 compose
->gtkaspell
->check_while_typing
)
11067 gtkaspell_highlight_all(compose
->gtkaspell
);
11071 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11073 Compose
*compose
= (Compose
*)data
;
11074 if (compose
->focused_editable
11075 #ifndef GENERIC_UMPC
11076 && gtk_widget_has_focus(compose
->focused_editable
)
11079 entry_allsel(compose
->focused_editable
);
11082 static void textview_move_beginning_of_line (GtkTextView
*text
)
11084 GtkTextBuffer
*buffer
;
11088 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11090 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11091 mark
= gtk_text_buffer_get_insert(buffer
);
11092 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11093 gtk_text_iter_set_line_offset(&ins
, 0);
11094 gtk_text_buffer_place_cursor(buffer
, &ins
);
11097 static void textview_move_forward_character (GtkTextView
*text
)
11099 GtkTextBuffer
*buffer
;
11103 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11105 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11106 mark
= gtk_text_buffer_get_insert(buffer
);
11107 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11108 if (gtk_text_iter_forward_cursor_position(&ins
))
11109 gtk_text_buffer_place_cursor(buffer
, &ins
);
11112 static void textview_move_backward_character (GtkTextView
*text
)
11114 GtkTextBuffer
*buffer
;
11118 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11120 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11121 mark
= gtk_text_buffer_get_insert(buffer
);
11122 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11123 if (gtk_text_iter_backward_cursor_position(&ins
))
11124 gtk_text_buffer_place_cursor(buffer
, &ins
);
11127 static void textview_move_forward_word (GtkTextView
*text
)
11129 GtkTextBuffer
*buffer
;
11134 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11136 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11137 mark
= gtk_text_buffer_get_insert(buffer
);
11138 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11139 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11140 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11141 gtk_text_iter_backward_word_start(&ins
);
11142 gtk_text_buffer_place_cursor(buffer
, &ins
);
11146 static void textview_move_backward_word (GtkTextView
*text
)
11148 GtkTextBuffer
*buffer
;
11152 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11154 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11155 mark
= gtk_text_buffer_get_insert(buffer
);
11156 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11157 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11158 gtk_text_buffer_place_cursor(buffer
, &ins
);
11161 static void textview_move_end_of_line (GtkTextView
*text
)
11163 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 if (gtk_text_iter_forward_to_line_end(&ins
))
11173 gtk_text_buffer_place_cursor(buffer
, &ins
);
11176 static void textview_move_next_line (GtkTextView
*text
)
11178 GtkTextBuffer
*buffer
;
11183 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11185 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11186 mark
= gtk_text_buffer_get_insert(buffer
);
11187 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11188 offset
= gtk_text_iter_get_line_offset(&ins
);
11189 if (gtk_text_iter_forward_line(&ins
)) {
11190 gtk_text_iter_set_line_offset(&ins
, offset
);
11191 gtk_text_buffer_place_cursor(buffer
, &ins
);
11195 static void textview_move_previous_line (GtkTextView
*text
)
11197 GtkTextBuffer
*buffer
;
11202 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11204 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11205 mark
= gtk_text_buffer_get_insert(buffer
);
11206 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11207 offset
= gtk_text_iter_get_line_offset(&ins
);
11208 if (gtk_text_iter_backward_line(&ins
)) {
11209 gtk_text_iter_set_line_offset(&ins
, offset
);
11210 gtk_text_buffer_place_cursor(buffer
, &ins
);
11214 static void textview_delete_forward_character (GtkTextView
*text
)
11216 GtkTextBuffer
*buffer
;
11218 GtkTextIter ins
, end_iter
;
11220 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11222 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11223 mark
= gtk_text_buffer_get_insert(buffer
);
11224 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11226 if (gtk_text_iter_forward_char(&end_iter
)) {
11227 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11231 static void textview_delete_backward_character (GtkTextView
*text
)
11233 GtkTextBuffer
*buffer
;
11235 GtkTextIter ins
, end_iter
;
11237 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11239 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11240 mark
= gtk_text_buffer_get_insert(buffer
);
11241 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11243 if (gtk_text_iter_backward_char(&end_iter
)) {
11244 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11248 static void textview_delete_forward_word (GtkTextView
*text
)
11250 GtkTextBuffer
*buffer
;
11252 GtkTextIter ins
, end_iter
;
11254 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11256 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11257 mark
= gtk_text_buffer_get_insert(buffer
);
11258 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11260 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11261 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11265 static void textview_delete_backward_word (GtkTextView
*text
)
11267 GtkTextBuffer
*buffer
;
11269 GtkTextIter ins
, end_iter
;
11271 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11273 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11274 mark
= gtk_text_buffer_get_insert(buffer
);
11275 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11277 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11278 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11282 static void textview_delete_line (GtkTextView
*text
)
11284 GtkTextBuffer
*buffer
;
11286 GtkTextIter ins
, start_iter
, end_iter
;
11288 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11290 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11291 mark
= gtk_text_buffer_get_insert(buffer
);
11292 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11295 gtk_text_iter_set_line_offset(&start_iter
, 0);
11298 if (gtk_text_iter_ends_line(&end_iter
)){
11299 if (!gtk_text_iter_forward_char(&end_iter
))
11300 gtk_text_iter_backward_char(&start_iter
);
11303 gtk_text_iter_forward_to_line_end(&end_iter
);
11304 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11307 static void textview_delete_to_line_end (GtkTextView
*text
)
11309 GtkTextBuffer
*buffer
;
11311 GtkTextIter ins
, end_iter
;
11313 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11315 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11316 mark
= gtk_text_buffer_get_insert(buffer
);
11317 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11319 if (gtk_text_iter_ends_line(&end_iter
))
11320 gtk_text_iter_forward_char(&end_iter
);
11322 gtk_text_iter_forward_to_line_end(&end_iter
);
11323 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11326 #define DO_ACTION(name, act) { \
11327 if(!strcmp(name, a_name)) { \
11331 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11333 const gchar
*a_name
= gtk_action_get_name(action
);
11334 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11335 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11336 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11337 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11338 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11339 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11340 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11341 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11342 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11343 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11344 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11345 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11346 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11347 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11348 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11351 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11353 Compose
*compose
= (Compose
*)data
;
11354 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11355 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11357 action
= compose_call_advanced_action_from_path(gaction
);
11360 void (*do_action
) (GtkTextView
*text
);
11361 } action_table
[] = {
11362 {textview_move_beginning_of_line
},
11363 {textview_move_forward_character
},
11364 {textview_move_backward_character
},
11365 {textview_move_forward_word
},
11366 {textview_move_backward_word
},
11367 {textview_move_end_of_line
},
11368 {textview_move_next_line
},
11369 {textview_move_previous_line
},
11370 {textview_delete_forward_character
},
11371 {textview_delete_backward_character
},
11372 {textview_delete_forward_word
},
11373 {textview_delete_backward_word
},
11374 {textview_delete_line
},
11375 {textview_delete_to_line_end
}
11378 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11380 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11381 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11382 if (action_table
[action
].do_action
)
11383 action_table
[action
].do_action(text
);
11385 g_warning("Not implemented yet.");
11389 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11391 GtkAllocation allocation
;
11395 if (GTK_IS_EDITABLE(widget
)) {
11396 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11397 gtk_editable_set_position(GTK_EDITABLE(widget
),
11400 if ((parent
= gtk_widget_get_parent(widget
))
11401 && (parent
= gtk_widget_get_parent(parent
))
11402 && (parent
= gtk_widget_get_parent(parent
))) {
11403 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11404 gtk_widget_get_allocation(widget
, &allocation
);
11405 gint y
= allocation
.y
;
11406 gint height
= allocation
.height
;
11407 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11408 (GTK_SCROLLED_WINDOW(parent
));
11410 gfloat value
= gtk_adjustment_get_value(shown
);
11411 gfloat upper
= gtk_adjustment_get_upper(shown
);
11412 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11413 if (y
< (int)value
) {
11414 gtk_adjustment_set_value(shown
, y
- 1);
11416 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11417 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11418 gtk_adjustment_set_value(shown
,
11419 y
+ height
- (int)page_size
- 1);
11421 gtk_adjustment_set_value(shown
,
11422 (int)upper
- (int)page_size
- 1);
11429 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11430 compose
->focused_editable
= widget
;
11432 #ifdef GENERIC_UMPC
11433 if (GTK_IS_TEXT_VIEW(widget
)
11434 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11435 g_object_ref(compose
->notebook
);
11436 g_object_ref(compose
->edit_vbox
);
11437 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11438 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11439 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11440 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11441 g_object_unref(compose
->notebook
);
11442 g_object_unref(compose
->edit_vbox
);
11443 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11444 G_CALLBACK(compose_grab_focus_cb
),
11446 gtk_widget_grab_focus(widget
);
11447 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11448 G_CALLBACK(compose_grab_focus_cb
),
11450 } else if (!GTK_IS_TEXT_VIEW(widget
)
11451 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11452 g_object_ref(compose
->notebook
);
11453 g_object_ref(compose
->edit_vbox
);
11454 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11455 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11456 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11457 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11458 g_object_unref(compose
->notebook
);
11459 g_object_unref(compose
->edit_vbox
);
11460 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11461 G_CALLBACK(compose_grab_focus_cb
),
11463 gtk_widget_grab_focus(widget
);
11464 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11465 G_CALLBACK(compose_grab_focus_cb
),
11471 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11473 compose
->modified
= TRUE
;
11474 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11475 #ifndef GENERIC_UMPC
11476 compose_set_title(compose
);
11480 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11482 Compose
*compose
= (Compose
*)data
;
11483 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11486 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11488 Compose
*compose
= (Compose
*)data
;
11489 compose_wrap_all_full(compose
, TRUE
);
11492 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11494 Compose
*compose
= (Compose
*)data
;
11496 message_search_compose(compose
);
11499 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11502 Compose
*compose
= (Compose
*)data
;
11503 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11504 if (compose
->autowrap
)
11505 compose_wrap_all_full(compose
, TRUE
);
11506 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11509 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11512 Compose
*compose
= (Compose
*)data
;
11513 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11516 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11518 Compose
*compose
= (Compose
*)data
;
11520 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11521 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11524 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11526 Compose
*compose
= (Compose
*)data
;
11528 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11529 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11532 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11534 g_free(compose
->privacy_system
);
11535 g_free(compose
->encdata
);
11537 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11538 compose_update_privacy_system_menu_item(compose
, warn
);
11541 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11543 if (folder_item
!= NULL
) {
11544 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11545 privacy_system_can_sign(compose
->privacy_system
)) {
11546 compose_use_signing(compose
,
11547 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11549 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11550 privacy_system_can_encrypt(compose
->privacy_system
)) {
11551 compose_use_encryption(compose
,
11552 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11557 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11559 Compose
*compose
= (Compose
*)data
;
11561 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11562 gtk_widget_show(compose
->ruler_hbox
);
11563 prefs_common
.show_ruler
= TRUE
;
11565 gtk_widget_hide(compose
->ruler_hbox
);
11566 gtk_widget_queue_resize(compose
->edit_vbox
);
11567 prefs_common
.show_ruler
= FALSE
;
11571 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11572 GdkDragContext
*context
,
11575 GtkSelectionData
*data
,
11578 gpointer user_data
)
11580 Compose
*compose
= (Compose
*)user_data
;
11584 type
= gtk_selection_data_get_data_type(data
);
11585 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11586 && gtk_drag_get_source_widget(context
) !=
11587 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11588 list
= uri_list_extract_filenames(
11589 (const gchar
*)gtk_selection_data_get_data(data
));
11590 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11591 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11592 compose_attach_append
11593 (compose
, (const gchar
*)tmp
->data
,
11594 utf8_filename
, NULL
, NULL
);
11595 g_free(utf8_filename
);
11598 compose_changed_cb(NULL
, compose
);
11599 list_free_strings_full(list
);
11600 } else if (gtk_drag_get_source_widget(context
)
11601 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11602 /* comes from our summaryview */
11603 SummaryView
* summaryview
= NULL
;
11604 GSList
* list
= NULL
, *cur
= NULL
;
11606 if (mainwindow_get_mainwindow())
11607 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11610 list
= summary_get_selected_msg_list(summaryview
);
11612 for (cur
= list
; cur
; cur
= cur
->next
) {
11613 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11614 gchar
*file
= NULL
;
11616 file
= procmsg_get_message_file_full(msginfo
,
11619 compose_attach_append(compose
, (const gchar
*)file
,
11620 (const gchar
*)file
, "message/rfc822", NULL
);
11624 g_slist_free(list
);
11628 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11629 GdkDragContext
*drag_context
,
11631 guint time
, gpointer user_data
)
11633 /* not handling this signal makes compose_insert_drag_received_cb
11638 static gboolean completion_set_focus_to_subject
11639 (GtkWidget
*widget
,
11640 GdkEventKey
*event
,
11643 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11645 /* make backtab move to subject field */
11646 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11647 gtk_widget_grab_focus(compose
->subject_entry
);
11653 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11654 GdkDragContext
*drag_context
,
11657 GtkSelectionData
*data
,
11660 gpointer user_data
)
11662 Compose
*compose
= (Compose
*)user_data
;
11668 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11670 type
= gtk_selection_data_get_data_type(data
);
11671 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11672 AlertValue val
= G_ALERTDEFAULT
;
11673 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11675 list
= uri_list_extract_filenames(ddata
);
11676 num_files
= g_list_length(list
);
11677 if (list
== NULL
&& strstr(ddata
, "://")) {
11678 /* Assume a list of no files, and data has ://, is a remote link */
11679 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11680 gchar
*tmpfile
= get_tmp_file();
11681 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11683 compose_insert_file(compose
, tmpfile
);
11684 claws_unlink(tmpfile
);
11686 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11687 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11690 switch (prefs_common
.compose_dnd_mode
) {
11691 case COMPOSE_DND_ASK
:
11692 msg
= g_strdup_printf(
11694 "Do you want to insert the contents of the file "
11695 "into the message body, or attach it to the email?",
11696 "Do you want to insert the contents of the %d files "
11697 "into the message body, or attach them to the email?",
11700 val
= alertpanel_full(_("Insert or attach?"), msg
,
11701 GTK_STOCK_CANCEL
, _("_Insert"), _("_Attach"),
11703 TRUE
, NULL
, ALERT_QUESTION
);
11706 case COMPOSE_DND_INSERT
:
11707 val
= G_ALERTALTERNATE
;
11709 case COMPOSE_DND_ATTACH
:
11710 val
= G_ALERTOTHER
;
11713 /* unexpected case */
11714 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11717 if (val
& G_ALERTDISABLE
) {
11718 val
&= ~G_ALERTDISABLE
;
11719 /* remember what action to perform by default, only if we don't click Cancel */
11720 if (val
== G_ALERTALTERNATE
)
11721 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11722 else if (val
== G_ALERTOTHER
)
11723 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11726 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11727 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11728 list_free_strings_full(list
);
11730 } else if (val
== G_ALERTOTHER
) {
11731 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11732 list_free_strings_full(list
);
11736 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11737 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11739 list_free_strings_full(list
);
11740 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11745 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11746 GdkDragContext
*drag_context
,
11749 GtkSelectionData
*data
,
11752 gpointer user_data
)
11754 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11755 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11757 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11760 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11761 gchar
*decoded
=g_new(gchar
, strlen(email
));
11764 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11765 gtk_editable_delete_text(entry
, 0, -1);
11766 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11767 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11771 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11774 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11776 Compose
*compose
= (Compose
*)data
;
11778 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11779 compose
->return_receipt
= TRUE
;
11781 compose
->return_receipt
= FALSE
;
11784 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11786 Compose
*compose
= (Compose
*)data
;
11788 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11789 compose
->remove_references
= TRUE
;
11791 compose
->remove_references
= FALSE
;
11794 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11795 ComposeHeaderEntry
*headerentry
)
11797 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11798 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11799 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11803 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11804 GdkEventKey
*event
,
11805 ComposeHeaderEntry
*headerentry
)
11807 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11808 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11809 !(event
->state
& GDK_MODIFIER_MASK
) &&
11810 (event
->keyval
== GDK_KEY_BackSpace
) &&
11811 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11812 gtk_container_remove
11813 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11814 headerentry
->combo
);
11815 gtk_container_remove
11816 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11817 headerentry
->entry
);
11818 headerentry
->compose
->header_list
=
11819 g_slist_remove(headerentry
->compose
->header_list
,
11821 g_free(headerentry
);
11822 } else if (event
->keyval
== GDK_KEY_Tab
) {
11823 if (headerentry
->compose
->header_last
== headerentry
) {
11824 /* Override default next focus, and give it to subject_entry
11825 * instead of notebook tabs
11827 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11828 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11835 static gboolean
scroll_postpone(gpointer data
)
11837 Compose
*compose
= (Compose
*)data
;
11839 if (compose
->batch
)
11842 GTK_EVENTS_FLUSH();
11843 compose_show_first_last_header(compose
, FALSE
);
11847 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11848 ComposeHeaderEntry
*headerentry
)
11850 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11851 compose_create_header_entry(headerentry
->compose
);
11852 g_signal_handlers_disconnect_matched
11853 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11854 0, 0, NULL
, NULL
, headerentry
);
11856 if (!headerentry
->compose
->batch
)
11857 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11861 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11863 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11864 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11868 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11870 GtkAdjustment
*vadj
;
11872 cm_return_if_fail(compose
);
11877 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11878 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11879 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11880 gtk_widget_get_parent(compose
->header_table
)));
11881 gtk_adjustment_set_value(vadj
, (show_first
?
11882 gtk_adjustment_get_lower(vadj
) :
11883 (gtk_adjustment_get_upper(vadj
) -
11884 gtk_adjustment_get_page_size(vadj
))));
11885 gtk_adjustment_changed(vadj
);
11888 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11889 const gchar
*text
, gint len
, Compose
*compose
)
11891 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11892 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11895 cm_return_if_fail(text
!= NULL
);
11897 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11898 G_CALLBACK(text_inserted
),
11900 if (paste_as_quotation
) {
11902 const gchar
*qmark
;
11904 GtkTextIter start_iter
;
11907 len
= strlen(text
);
11909 new_text
= g_strndup(text
, len
);
11911 qmark
= compose_quote_char_from_context(compose
);
11913 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11914 gtk_text_buffer_place_cursor(buffer
, iter
);
11916 pos
= gtk_text_iter_get_offset(iter
);
11918 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11919 _("Quote format error at line %d."));
11920 quote_fmt_reset_vartable();
11922 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11923 GINT_TO_POINTER(paste_as_quotation
- 1));
11925 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11926 gtk_text_buffer_place_cursor(buffer
, iter
);
11927 gtk_text_buffer_delete_mark(buffer
, mark
);
11929 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11930 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11931 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11932 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11933 gtk_text_buffer_delete_mark(buffer
, mark
);
11935 if (strcmp(text
, "\n") || compose
->automatic_break
11936 || gtk_text_iter_starts_line(iter
)) {
11937 GtkTextIter before_ins
;
11938 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11939 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11940 before_ins
= *iter
;
11941 gtk_text_iter_backward_chars(&before_ins
, len
);
11942 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11945 /* check if the preceding is just whitespace or quote */
11946 GtkTextIter start_line
;
11947 gchar
*tmp
= NULL
, *quote
= NULL
;
11948 gint quote_len
= 0, is_normal
= 0;
11949 start_line
= *iter
;
11950 gtk_text_iter_set_line_offset(&start_line
, 0);
11951 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11954 if (*tmp
== '\0') {
11957 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11965 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11967 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11968 iter
, text
, len
, "no_join", NULL
);
11973 if (!paste_as_quotation
) {
11974 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11975 compose_beautify_paragraph(compose
, iter
, FALSE
);
11976 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11977 gtk_text_buffer_delete_mark(buffer
, mark
);
11980 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
11981 G_CALLBACK(text_inserted
),
11983 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
11985 if (compose_can_autosave(compose
) &&
11986 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
11987 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
11988 compose
->draft_timeout_tag
= g_timeout_add
11989 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
11993 static void compose_check_all(GtkAction
*action
, gpointer data
)
11995 Compose
*compose
= (Compose
*)data
;
11996 if (!compose
->gtkaspell
)
11999 if (gtk_widget_has_focus(compose
->subject_entry
))
12000 claws_spell_entry_check_all(
12001 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12003 gtkaspell_check_all(compose
->gtkaspell
);
12006 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12008 Compose
*compose
= (Compose
*)data
;
12009 if (compose
->gtkaspell
) {
12010 claws_spell_entry_recheck_all(
12011 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12012 gtkaspell_highlight_all(compose
->gtkaspell
);
12016 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12018 Compose
*compose
= (Compose
*)data
;
12019 if (!compose
->gtkaspell
) {
12020 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12024 if (gtk_widget_has_focus(compose
->subject_entry
))
12025 claws_spell_entry_check_backwards(
12026 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12028 gtkaspell_check_backwards(compose
->gtkaspell
);
12031 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12033 Compose
*compose
= (Compose
*)data
;
12034 if (!compose
->gtkaspell
) {
12035 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12039 if (gtk_widget_has_focus(compose
->subject_entry
))
12040 claws_spell_entry_check_forwards_go(
12041 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12043 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12048 *\brief Guess originating forward account from MsgInfo and several
12049 * "common preference" settings. Return NULL if no guess.
12051 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12053 PrefsAccount
*account
= NULL
;
12055 cm_return_val_if_fail(msginfo
, NULL
);
12056 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12057 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12059 if (msginfo
->folder
->prefs
->enable_default_account
)
12060 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12062 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12064 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12065 extract_address(to
);
12066 account
= account_find_from_address(to
, FALSE
);
12069 if (!account
&& prefs_common
.forward_account_autosel
) {
12071 if (!procheader_get_header_from_msginfo
12072 (msginfo
, &cc
, "Cc:")) {
12073 gchar
*buf
= cc
+ strlen("Cc:");
12074 extract_address(buf
);
12075 account
= account_find_from_address(buf
, FALSE
);
12080 if (!account
&& prefs_common
.forward_account_autosel
) {
12081 gchar
*deliveredto
= NULL
;
12082 if (!procheader_get_header_from_msginfo
12083 (msginfo
, &deliveredto
, "Delivered-To:")) {
12084 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12085 extract_address(buf
);
12086 account
= account_find_from_address(buf
, FALSE
);
12087 g_free(deliveredto
);
12092 account
= msginfo
->folder
->folder
->account
;
12097 gboolean
compose_close(Compose
*compose
)
12101 cm_return_val_if_fail(compose
, FALSE
);
12103 if (!g_mutex_trylock(compose
->mutex
)) {
12104 /* we have to wait for the (possibly deferred by auto-save)
12105 * drafting to be done, before destroying the compose under
12107 debug_print("waiting for drafting to finish...\n");
12108 compose_allow_user_actions(compose
, FALSE
);
12109 if (compose
->close_timeout_tag
== 0) {
12110 compose
->close_timeout_tag
=
12111 g_timeout_add (500, (GSourceFunc
) compose_close
,
12117 if (compose
->draft_timeout_tag
>= 0) {
12118 g_source_remove(compose
->draft_timeout_tag
);
12119 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12122 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
12123 if (!compose
->batch
) {
12124 prefs_common
.compose_x
= x
;
12125 prefs_common
.compose_y
= y
;
12127 g_mutex_unlock(compose
->mutex
);
12128 compose_destroy(compose
);
12133 * Add entry field for each address in list.
12134 * \param compose E-Mail composition object.
12135 * \param listAddress List of (formatted) E-Mail addresses.
12137 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12140 node
= listAddress
;
12142 addr
= ( gchar
* ) node
->data
;
12143 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12144 node
= g_list_next( node
);
12148 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12149 guint action
, gboolean opening_multiple
)
12151 gchar
*body
= NULL
;
12152 GSList
*new_msglist
= NULL
;
12153 MsgInfo
*tmp_msginfo
= NULL
;
12154 gboolean originally_enc
= FALSE
;
12155 gboolean originally_sig
= FALSE
;
12156 Compose
*compose
= NULL
;
12157 gchar
*s_system
= NULL
;
12159 cm_return_if_fail(msginfo_list
!= NULL
);
12161 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12162 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12163 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12165 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12166 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12167 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12168 orig_msginfo
, mimeinfo
);
12169 if (tmp_msginfo
!= NULL
) {
12170 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12172 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12173 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12174 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12176 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12177 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12178 if (orig_msginfo
->tags
) {
12179 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12180 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12186 if (!opening_multiple
&& msgview
!= NULL
)
12187 body
= messageview_get_selection(msgview
);
12190 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12191 procmsg_msginfo_free(&tmp_msginfo
);
12192 g_slist_free(new_msglist
);
12194 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12196 if (compose
&& originally_enc
) {
12197 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12200 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12201 compose_force_signing(compose
, compose
->account
, s_system
);
12205 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12208 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12211 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12212 && msginfo_list
!= NULL
12213 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12214 GSList
*cur
= msginfo_list
;
12215 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12216 "messages. Opening the windows "
12217 "could take some time. Do you "
12218 "want to continue?"),
12219 g_slist_length(msginfo_list
));
12220 if (g_slist_length(msginfo_list
) > 9
12221 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_YES
, NULL
,
12222 ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12227 /* We'll open multiple compose windows */
12228 /* let the WM place the next windows */
12229 compose_force_window_origin
= FALSE
;
12230 for (; cur
; cur
= cur
->next
) {
12232 tmplist
.data
= cur
->data
;
12233 tmplist
.next
= NULL
;
12234 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12236 compose_force_window_origin
= TRUE
;
12238 /* forwarding multiple mails as attachments is done via a
12239 * single compose window */
12240 if (msginfo_list
!= NULL
) {
12241 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12242 } else if (msgview
!= NULL
) {
12244 tmplist
.data
= msgview
->msginfo
;
12245 tmplist
.next
= NULL
;
12246 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12248 debug_print("Nothing to reply to\n");
12253 void compose_check_for_email_account(Compose
*compose
)
12255 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12261 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12262 ac
= account_get_cur_account();
12263 if (ac
->protocol
== A_NNTP
) {
12264 list
= account_get_list();
12266 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12267 curr
= (PrefsAccount
*) list
->data
;
12268 if (curr
->protocol
!= A_NNTP
) {
12274 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12279 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12280 const gchar
*address
)
12282 GSList
*msginfo_list
= NULL
;
12283 gchar
*body
= messageview_get_selection(msgview
);
12286 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12288 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12289 compose_check_for_email_account(compose
);
12290 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12291 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12292 compose_reply_set_subject(compose
, msginfo
);
12295 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12298 void compose_set_position(Compose
*compose
, gint pos
)
12300 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12302 gtkut_text_view_set_position(text
, pos
);
12305 gboolean
compose_search_string(Compose
*compose
,
12306 const gchar
*str
, gboolean case_sens
)
12308 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12310 return gtkut_text_view_search_string(text
, str
, case_sens
);
12313 gboolean
compose_search_string_backward(Compose
*compose
,
12314 const gchar
*str
, gboolean case_sens
)
12316 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12318 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12321 /* allocate a msginfo structure and populate its data from a compose data structure */
12322 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12324 MsgInfo
*newmsginfo
;
12326 gchar date
[RFC822_DATE_BUFFSIZE
];
12328 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12330 newmsginfo
= procmsg_msginfo_new();
12333 get_rfc822_date(date
, sizeof(date
));
12334 newmsginfo
->date
= g_strdup(date
);
12337 if (compose
->from_name
) {
12338 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12339 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12343 if (compose
->subject_entry
)
12344 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12346 /* to, cc, reply-to, newsgroups */
12347 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12348 gchar
*header
= gtk_editable_get_chars(
12350 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12351 gchar
*entry
= gtk_editable_get_chars(
12352 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12354 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12355 if ( newmsginfo
->to
== NULL
) {
12356 newmsginfo
->to
= g_strdup(entry
);
12357 } else if (entry
&& *entry
) {
12358 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12359 g_free(newmsginfo
->to
);
12360 newmsginfo
->to
= tmp
;
12363 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12364 if ( newmsginfo
->cc
== NULL
) {
12365 newmsginfo
->cc
= g_strdup(entry
);
12366 } else if (entry
&& *entry
) {
12367 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12368 g_free(newmsginfo
->cc
);
12369 newmsginfo
->cc
= tmp
;
12372 if ( strcasecmp(header
,
12373 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12374 if ( newmsginfo
->newsgroups
== NULL
) {
12375 newmsginfo
->newsgroups
= g_strdup(entry
);
12376 } else if (entry
&& *entry
) {
12377 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12378 g_free(newmsginfo
->newsgroups
);
12379 newmsginfo
->newsgroups
= tmp
;
12387 /* other data is unset */
12393 /* update compose's dictionaries from folder dict settings */
12394 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12395 FolderItem
*folder_item
)
12397 cm_return_if_fail(compose
!= NULL
);
12399 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12400 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12402 if (prefs
->enable_default_dictionary
)
12403 gtkaspell_change_dict(compose
->gtkaspell
,
12404 prefs
->default_dictionary
, FALSE
);
12405 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12406 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12407 prefs
->default_alt_dictionary
);
12408 if (prefs
->enable_default_dictionary
12409 || prefs
->enable_default_alt_dictionary
)
12410 compose_spell_menu_changed(compose
);
12415 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12417 Compose
*compose
= (Compose
*)data
;
12419 cm_return_if_fail(compose
!= NULL
);
12421 gtk_widget_grab_focus(compose
->text
);
12424 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12426 gtk_combo_box_popup(GTK_COMBO_BOX(data
));