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. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
110 #include "file-utils.h"
113 #include "password.h"
114 #include "ldapserver.h"
128 #define N_ATTACH_COLS (N_COL_COLUMNS)
132 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
= -1,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
= 0,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
135 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
141 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
142 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
143 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
145 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
147 } ComposeCallAdvancedAction
;
151 PRIORITY_HIGHEST
= 1,
160 COMPOSE_INSERT_SUCCESS
,
161 COMPOSE_INSERT_READ_ERROR
,
162 COMPOSE_INSERT_INVALID_CHARACTER
,
163 COMPOSE_INSERT_NO_FILE
164 } ComposeInsertResult
;
168 COMPOSE_WRITE_FOR_SEND
,
169 COMPOSE_WRITE_FOR_STORE
174 COMPOSE_QUOTE_FORCED
,
181 SUBJECT_FIELD_PRESENT
,
186 #define B64_LINE_SIZE 57
187 #define B64_BUFFSIZE 77
189 #define MAX_REFERENCES_LEN 999
191 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
192 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
194 #define COMPOSE_PRIVACY_WARNING() { \
195 alertpanel_error(_("You have opted to sign and/or encrypt this " \
196 "message but have not selected a privacy system.\n\n" \
197 "Signing and encrypting have been disabled for this " \
201 static GdkColor default_header_bgcolor
= {
208 static GdkColor default_header_color
= {
215 static GList
*compose_list
= NULL
;
216 static GSList
*extra_headers
= NULL
;
218 static Compose
*compose_generic_new (PrefsAccount
*account
,
222 GList
*listAddress
);
224 static Compose
*compose_create (PrefsAccount
*account
,
229 static void compose_entry_indicate (Compose
*compose
,
230 const gchar
*address
);
231 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
232 ComposeQuoteMode quote_mode
,
236 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
237 GSList
*msginfo_list
);
238 static Compose
*compose_reply (MsgInfo
*msginfo
,
239 ComposeQuoteMode quote_mode
,
244 static Compose
*compose_reply_mode (ComposeMode mode
,
245 GSList
*msginfo_list
,
247 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
248 static void compose_update_privacy_systems_menu(Compose
*compose
);
250 static GtkWidget
*compose_account_option_menu_create
252 static void compose_set_out_encoding (Compose
*compose
);
253 static void compose_set_template_menu (Compose
*compose
);
254 static void compose_destroy (Compose
*compose
);
256 static MailField
compose_entries_set (Compose
*compose
,
258 ComposeEntryType to_type
);
259 static gint
compose_parse_header (Compose
*compose
,
261 static gint
compose_parse_manual_headers (Compose
*compose
,
263 HeaderEntry
*entries
);
264 static gchar
*compose_parse_references (const gchar
*ref
,
267 static gchar
*compose_quote_fmt (Compose
*compose
,
273 gboolean need_unescape
,
274 const gchar
*err_msg
);
276 static void compose_reply_set_entry (Compose
*compose
,
282 followup_and_reply_to
);
283 static void compose_reedit_set_entry (Compose
*compose
,
286 static void compose_insert_sig (Compose
*compose
,
288 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
291 static gboolean
compose_attach_append (Compose
*compose
,
294 const gchar
*content_type
,
295 const gchar
*charset
);
296 static void compose_attach_parts (Compose
*compose
,
299 static gboolean
compose_beautify_paragraph (Compose
*compose
,
300 GtkTextIter
*par_iter
,
302 static void compose_wrap_all (Compose
*compose
);
303 static void compose_wrap_all_full (Compose
*compose
,
306 static void compose_set_title (Compose
*compose
);
307 static void compose_select_account (Compose
*compose
,
308 PrefsAccount
*account
,
311 static PrefsAccount
*compose_current_mail_account(void);
312 /* static gint compose_send (Compose *compose); */
313 static gboolean compose_check_for_valid_recipient
315 static gboolean
compose_check_entries (Compose
*compose
,
316 gboolean check_everything
);
317 static gint
compose_write_to_file (Compose
*compose
,
320 gboolean attach_parts
);
321 static gint
compose_write_body_to_file (Compose
*compose
,
323 static gint
compose_remove_reedit_target (Compose
*compose
,
325 static void compose_remove_draft (Compose
*compose
);
326 static ComposeQueueResult
compose_queue_sub (Compose
*compose
,
330 gboolean perform_checks
,
331 gboolean remove_reedit_target
);
332 static int compose_add_attachments (Compose
*compose
,
334 static gchar
*compose_get_header (Compose
*compose
);
335 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
337 static void compose_convert_header (Compose
*compose
,
342 gboolean addr_field
);
344 static void compose_attach_info_free (AttachInfo
*ainfo
);
345 static void compose_attach_remove_selected (GtkAction
*action
,
348 static void compose_template_apply (Compose
*compose
,
351 static void compose_attach_property (GtkAction
*action
,
353 static void compose_attach_property_create (gboolean
*cancelled
);
354 static void attach_property_ok (GtkWidget
*widget
,
355 gboolean
*cancelled
);
356 static void attach_property_cancel (GtkWidget
*widget
,
357 gboolean
*cancelled
);
358 static gint
attach_property_delete_event (GtkWidget
*widget
,
360 gboolean
*cancelled
);
361 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
363 gboolean
*cancelled
);
365 static void compose_exec_ext_editor (Compose
*compose
);
367 static gint
compose_exec_ext_editor_real (const gchar
*file
,
368 GdkNativeWindow socket_wid
);
369 static gboolean
compose_ext_editor_kill (Compose
*compose
);
370 static gboolean
compose_input_cb (GIOChannel
*source
,
371 GIOCondition condition
,
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();
377 static gboolean compose_ext_editor_plug_removed_cb
380 #endif /* G_OS_UNIX */
382 static void compose_undo_state_changed (UndoMain
*undostruct
,
387 static void compose_create_header_entry (Compose
*compose
);
388 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
389 gchar
*text
, ComposePrefType pref_type
);
390 static void compose_remove_header_entries(Compose
*compose
);
392 static void compose_update_priority_menu_item(Compose
* compose
);
394 static void compose_spell_menu_changed (void *data
);
395 static void compose_dict_changed (void *data
);
397 static void compose_add_field_list ( Compose
*compose
,
398 GList
*listAddress
);
400 /* callback functions */
402 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
403 GtkAllocation
*allocation
,
405 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
406 GtkAllocation
*allocation
,
407 GtkSHRuler
*shruler
);
408 static void account_activated (GtkComboBox
*optmenu
,
410 static void attach_selected (GtkTreeView
*tree_view
,
411 GtkTreePath
*tree_path
,
412 GtkTreeViewColumn
*column
,
414 static gboolean
attach_button_pressed (GtkWidget
*widget
,
415 GdkEventButton
*event
,
417 static gboolean
attach_key_pressed (GtkWidget
*widget
,
420 static void compose_send_cb (GtkAction
*action
, gpointer data
);
421 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
423 static void compose_save_cb (GtkAction
*action
,
426 static void compose_attach_cb (GtkAction
*action
,
428 static void compose_insert_file_cb (GtkAction
*action
,
430 static void compose_insert_sig_cb (GtkAction
*action
,
432 static void compose_replace_sig_cb (GtkAction
*action
,
435 static void compose_close_cb (GtkAction
*action
,
437 static void compose_print_cb (GtkAction
*action
,
440 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
442 static void compose_address_cb (GtkAction
*action
,
444 static void about_show_cb (GtkAction
*action
,
446 static void compose_template_activate_cb(GtkWidget
*widget
,
449 static void compose_ext_editor_cb (GtkAction
*action
,
452 static gint
compose_delete_cb (GtkWidget
*widget
,
456 static void compose_undo_cb (GtkAction
*action
,
458 static void compose_redo_cb (GtkAction
*action
,
460 static void compose_cut_cb (GtkAction
*action
,
462 static void compose_copy_cb (GtkAction
*action
,
464 static void compose_paste_cb (GtkAction
*action
,
466 static void compose_paste_as_quote_cb (GtkAction
*action
,
468 static void compose_paste_no_wrap_cb (GtkAction
*action
,
470 static void compose_paste_wrap_cb (GtkAction
*action
,
472 static void compose_allsel_cb (GtkAction
*action
,
475 static void compose_advanced_action_cb (GtkAction
*action
,
478 static void compose_grab_focus_cb (GtkWidget
*widget
,
481 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
484 static void compose_wrap_cb (GtkAction
*action
,
486 static void compose_wrap_all_cb (GtkAction
*action
,
488 static void compose_find_cb (GtkAction
*action
,
490 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
492 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
495 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
497 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
499 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
501 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
502 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
503 static void compose_activate_privacy_system (Compose
*compose
,
504 PrefsAccount
*account
,
506 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
);
507 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
509 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
511 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
512 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
513 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
515 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
516 GdkDragContext
*drag_context
,
519 GtkSelectionData
*data
,
523 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
524 GdkDragContext
*drag_context
,
527 GtkSelectionData
*data
,
531 static void compose_header_drag_received_cb (GtkWidget
*widget
,
532 GdkDragContext
*drag_context
,
535 GtkSelectionData
*data
,
540 static gboolean
compose_drag_drop (GtkWidget
*widget
,
541 GdkDragContext
*drag_context
,
543 guint time
, gpointer user_data
);
544 static gboolean completion_set_focus_to_subject
549 static void text_inserted (GtkTextBuffer
*buffer
,
554 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
555 ComposeQuoteMode quote_mode
,
559 gboolean followup_and_reply_to
,
562 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
563 ComposeHeaderEntry
*headerentry
);
564 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
566 ComposeHeaderEntry
*headerentry
);
567 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
568 ComposeHeaderEntry
*headerentry
);
570 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
572 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
574 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
580 static void compose_check_all (GtkAction
*action
, gpointer data
);
581 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
582 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
583 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
586 static PrefsAccount
*compose_find_account (MsgInfo
*msginfo
);
588 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
591 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
592 FolderItem
*folder_item
);
594 static void compose_attach_update_label(Compose
*compose
);
595 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
596 gboolean respect_default_to
);
597 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
598 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
);
600 static GtkActionEntry compose_popup_entries
[] =
602 {"Compose", NULL
, "Compose", NULL
, NULL
, NULL
},
603 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
604 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
605 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
606 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
609 static GtkActionEntry compose_entries
[] =
611 {"Menu", NULL
, "Menu", NULL
, NULL
, NULL
},
613 {"Message", NULL
, N_("_Message"), NULL
, NULL
, NULL
},
614 {"Edit", NULL
, N_("_Edit"), NULL
, NULL
, NULL
},
616 {"Spelling", NULL
, N_("_Spelling"), NULL
, NULL
, NULL
},
618 {"Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
619 {"Tools", NULL
, N_("_Tools"), NULL
, NULL
, NULL
},
620 {"Help", NULL
, N_("_Help"), NULL
, NULL
, NULL
},
622 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
623 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
624 {"Message/---", NULL
, "---", NULL
, NULL
, NULL
},
626 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
627 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
628 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
629 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
630 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
631 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
632 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
633 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
634 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
635 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
638 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
639 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
640 {"Edit/---", NULL
, "---", NULL
, NULL
, NULL
},
642 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
643 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
644 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
646 {"Edit/SpecialPaste", NULL
, N_("_Special paste"), NULL
, NULL
, NULL
},
647 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
648 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
649 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
651 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
653 {"Edit/Advanced", NULL
, N_("A_dvanced"), NULL
, NULL
, NULL
},
654 {"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*/
655 {"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*/
656 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
657 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
658 {"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*/
659 {"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*/
660 {"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*/
661 {"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*/
662 {"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*/
663 {"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*/
664 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
665 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
666 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
667 {"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*/
669 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
670 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
672 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
673 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
674 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
675 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
676 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
679 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
680 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
681 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
682 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
684 {"Spelling/---", NULL
, "---", NULL
, NULL
, NULL
},
685 {"Spelling/Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
689 {"Options/ReplyMode", NULL
, N_("Reply _mode"), NULL
, NULL
, NULL
},
690 {"Options/---", NULL
, "---", NULL
, NULL
, NULL
},
691 {"Options/PrivacySystem", NULL
, N_("Privacy _System"), NULL
, NULL
, NULL
},
692 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
694 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
695 {"Options/Priority", NULL
, N_("_Priority"), NULL
, NULL
, NULL
},
697 {"Options/Encoding", NULL
, N_("Character _encoding"), NULL
, NULL
, NULL
},
698 {"Options/Encoding/---", NULL
, "---", NULL
, NULL
, NULL
},
699 #define ENC_ACTION(cs_char,c_char,string) \
700 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
702 {"Options/Encoding/Western", NULL
, N_("Western European"), NULL
, NULL
, NULL
},
703 {"Options/Encoding/Baltic", NULL
, N_("Baltic"), NULL
, NULL
, NULL
},
704 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew"), NULL
, NULL
, NULL
},
705 {"Options/Encoding/Arabic", NULL
, N_("Arabic"), NULL
, NULL
, NULL
},
706 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic"), NULL
, NULL
, NULL
},
707 {"Options/Encoding/Japanese", NULL
, N_("Japanese"), NULL
, NULL
, NULL
},
708 {"Options/Encoding/Chinese", NULL
, N_("Chinese"), NULL
, NULL
, NULL
},
709 {"Options/Encoding/Korean", NULL
, N_("Korean"), NULL
, NULL
, NULL
},
710 {"Options/Encoding/Thai", NULL
, N_("Thai"), NULL
, NULL
, NULL
},
713 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
715 {"Tools/Template", NULL
, N_("_Template"), NULL
, NULL
, NULL
},
716 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
717 {"Tools/Actions", NULL
, N_("Actio_ns"), NULL
, NULL
, NULL
},
718 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
721 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
724 static GtkToggleActionEntry compose_toggle_entries
[] =
726 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
), FALSE
}, /* Toggle */
727 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
), FALSE
}, /* Toggle */
728 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
), FALSE
}, /* Toggle */
729 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
), FALSE
}, /* Toggle */
730 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
), FALSE
}, /* Toggle */
731 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
), FALSE
}, /* Toggle */
732 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
), FALSE
}, /* Toggle */
735 static GtkRadioActionEntry compose_radio_rm_entries
[] =
737 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
738 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
739 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
740 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
743 static GtkRadioActionEntry compose_radio_prio_entries
[] =
745 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
746 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
747 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
748 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
749 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
752 static GtkRadioActionEntry compose_radio_enc_entries
[] =
754 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Cyrillic/"CS_MACCYR
, C_MACCYR
, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
780 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
781 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
782 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
783 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
784 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
785 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
786 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
789 static GtkTargetEntry compose_mime_types
[] =
791 {"text/uri-list", 0, 0},
792 {"UTF8_STRING", 0, 0},
796 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
798 const GList
*compose_list
= compose_get_compose_list();
799 const GList
*elem
= NULL
;
802 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
804 Compose
*c
= (Compose
*)elem
->data
;
806 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
810 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
811 gtkut_window_popup(c
->window
);
819 static GdkColor quote_color1
=
820 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
821 static GdkColor quote_color2
=
822 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
823 static GdkColor quote_color3
=
824 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
826 static GdkColor quote_bgcolor1
=
827 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
828 static GdkColor quote_bgcolor2
=
829 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
830 static GdkColor quote_bgcolor3
=
831 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
833 static GdkColor signature_color
= {
840 static GdkColor uri_color
= {
847 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
849 GtkTextBuffer
*buffer
;
850 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
852 buffer
= gtk_text_view_get_buffer(text
);
854 if (prefs_common
.enable_color
) {
855 /* grab the quote colors, converting from an int to a GdkColor */
856 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1
],
858 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2
],
860 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3
],
862 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1_BG
],
864 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2_BG
],
866 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3_BG
],
868 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_SIGNATURE
],
870 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_URI
],
873 signature_color
= quote_color1
= quote_color2
= quote_color3
=
874 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
877 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
878 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
879 "foreground-gdk", "e_color1
,
880 "paragraph-background-gdk", "e_bgcolor1
,
882 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
883 "foreground-gdk", "e_color2
,
884 "paragraph-background-gdk", "e_bgcolor2
,
886 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
887 "foreground-gdk", "e_color3
,
888 "paragraph-background-gdk", "e_bgcolor3
,
891 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
892 "foreground-gdk", "e_color1
,
894 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
895 "foreground-gdk", "e_color2
,
897 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
898 "foreground-gdk", "e_color3
,
902 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
903 "foreground-gdk", &signature_color
,
906 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
907 "foreground-gdk", &uri_color
,
909 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
910 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
913 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
916 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
919 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
921 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
924 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
926 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
929 #define SCROLL_TO_CURSOR(compose) { \
930 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
931 gtk_text_view_get_buffer( \
932 GTK_TEXT_VIEW(compose->text))); \
933 gtk_text_view_scroll_mark_onscreen( \
934 GTK_TEXT_VIEW(compose->text), \
938 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
941 if (folderidentifier
) {
942 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
943 prefs_common
.compose_save_to_history
= add_history(
944 prefs_common
.compose_save_to_history
, folderidentifier
);
945 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
946 prefs_common
.compose_save_to_history
);
949 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
950 if (folderidentifier
)
951 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
953 gtk_entry_set_text(GTK_ENTRY(entry
), "");
956 static gchar
*compose_get_save_to(Compose
*compose
)
959 gchar
*result
= NULL
;
960 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
961 result
= gtk_editable_get_chars(entry
, 0, -1);
964 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
965 prefs_common
.compose_save_to_history
= add_history(
966 prefs_common
.compose_save_to_history
, result
);
967 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
968 prefs_common
.compose_save_to_history
);
973 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
974 GList
*attach_files
, GList
*listAddress
)
977 GtkTextView
*textview
;
978 GtkTextBuffer
*textbuf
;
980 const gchar
*subject_format
= NULL
;
981 const gchar
*body_format
= NULL
;
982 gchar
*mailto_from
= NULL
;
983 PrefsAccount
*mailto_account
= NULL
;
984 MsgInfo
* dummyinfo
= NULL
;
985 gint cursor_pos
= -1;
986 MailField mfield
= NO_FIELD_PRESENT
;
990 /* check if mailto defines a from */
991 if (mailto
&& *mailto
!= '\0') {
992 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
993 /* mailto defines a from, check if we can get account prefs from it,
994 if not, the account prefs will be guessed using other ways, but we'll keep
997 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
998 if (mailto_account
== NULL
) {
1000 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
1001 extract_address(tmp_from
);
1002 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
1006 account
= mailto_account
;
1009 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1010 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
1011 account
= account_find_from_id(item
->prefs
->default_account
);
1013 /* if no account prefs set, fallback to the current one */
1014 if (!account
) account
= cur_account
;
1015 cm_return_val_if_fail(account
!= NULL
, NULL
);
1017 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1018 compose_apply_folder_privacy_settings(compose
, item
);
1020 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1021 (account
->default_encrypt
|| account
->default_sign
))
1022 COMPOSE_PRIVACY_WARNING();
1024 /* override from name if mailto asked for it */
1026 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1027 g_free(mailto_from
);
1029 /* override from name according to folder properties */
1030 if (item
&& item
->prefs
&&
1031 item
->prefs
->compose_with_format
&&
1032 item
->prefs
->compose_override_from_format
&&
1033 *item
->prefs
->compose_override_from_format
!= '\0') {
1038 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1040 /* decode \-escape sequences in the internal representation of the quote format */
1041 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1042 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1045 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1046 compose
->gtkaspell
);
1048 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1050 quote_fmt_scan_string(tmp
);
1053 buf
= quote_fmt_get_buffer();
1055 alertpanel_error(_("New message From format error."));
1057 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1058 quote_fmt_reset_vartable();
1059 quote_fmtlex_destroy();
1064 compose
->replyinfo
= NULL
;
1065 compose
->fwdinfo
= NULL
;
1067 textview
= GTK_TEXT_VIEW(compose
->text
);
1068 textbuf
= gtk_text_view_get_buffer(textview
);
1069 compose_create_tags(textview
, compose
);
1071 undo_block(compose
->undostruct
);
1073 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1076 if (account
->auto_sig
)
1077 compose_insert_sig(compose
, FALSE
);
1078 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1079 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1081 if (account
->protocol
!= A_NNTP
) {
1082 if (mailto
&& *mailto
!= '\0') {
1083 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1086 compose_set_folder_prefs(compose
, item
, TRUE
);
1088 if (item
&& item
->ret_rcpt
) {
1089 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1092 if (mailto
&& *mailto
!= '\0') {
1093 if (!strchr(mailto
, '@'))
1094 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1096 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1097 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1098 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1099 mfield
= TO_FIELD_PRESENT
;
1102 * CLAWS: just don't allow return receipt request, even if the user
1103 * may want to send an email. simple but foolproof.
1105 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1107 compose_add_field_list( compose
, listAddress
);
1109 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1110 subject_format
= item
->prefs
->compose_subject_format
;
1111 body_format
= item
->prefs
->compose_body_format
;
1112 } else if (account
->compose_with_format
) {
1113 subject_format
= account
->compose_subject_format
;
1114 body_format
= account
->compose_body_format
;
1115 } else if (prefs_common
.compose_with_format
) {
1116 subject_format
= prefs_common
.compose_subject_format
;
1117 body_format
= prefs_common
.compose_body_format
;
1120 if (subject_format
|| body_format
) {
1123 && *subject_format
!= '\0' )
1125 gchar
*subject
= NULL
;
1130 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1132 /* decode \-escape sequences in the internal representation of the quote format */
1133 tmp
= g_malloc(strlen(subject_format
)+1);
1134 pref_get_unescaped_pref(tmp
, subject_format
);
1136 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1138 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1139 compose
->gtkaspell
);
1141 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1143 quote_fmt_scan_string(tmp
);
1146 buf
= quote_fmt_get_buffer();
1148 alertpanel_error(_("New message subject format error."));
1150 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1151 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1152 quote_fmt_reset_vartable();
1153 quote_fmtlex_destroy();
1157 mfield
= SUBJECT_FIELD_PRESENT
;
1161 && *body_format
!= '\0' )
1164 GtkTextBuffer
*buffer
;
1165 GtkTextIter start
, end
;
1169 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1171 text
= GTK_TEXT_VIEW(compose
->text
);
1172 buffer
= gtk_text_view_get_buffer(text
);
1173 gtk_text_buffer_get_start_iter(buffer
, &start
);
1174 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1175 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1177 compose_quote_fmt(compose
, dummyinfo
,
1179 NULL
, tmp
, FALSE
, TRUE
,
1180 _("The body of the \"New message\" template has an error at line %d."));
1181 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1182 quote_fmt_reset_vartable();
1186 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1187 gtkaspell_highlight_all(compose
->gtkaspell
);
1189 mfield
= BODY_FIELD_PRESENT
;
1193 procmsg_msginfo_free( &dummyinfo
);
1199 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1200 ainfo
= (AttachInfo
*) curr
->data
;
1202 compose_insert_file(compose
, ainfo
->file
);
1204 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1205 ainfo
->content_type
, ainfo
->charset
);
1209 compose_show_first_last_header(compose
, TRUE
);
1211 /* Set save folder */
1212 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1213 gchar
*folderidentifier
;
1215 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1216 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1217 folderidentifier
= folder_item_get_identifier(item
);
1218 compose_set_save_to(compose
, folderidentifier
);
1219 g_free(folderidentifier
);
1222 /* Place cursor according to provided input (mfield) */
1224 case NO_FIELD_PRESENT
:
1225 if (compose
->header_last
)
1226 gtk_widget_grab_focus(compose
->header_last
->entry
);
1228 case TO_FIELD_PRESENT
:
1229 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1231 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1234 gtk_widget_grab_focus(compose
->subject_entry
);
1236 case SUBJECT_FIELD_PRESENT
:
1237 textview
= GTK_TEXT_VIEW(compose
->text
);
1240 textbuf
= gtk_text_view_get_buffer(textview
);
1243 mark
= gtk_text_buffer_get_insert(textbuf
);
1244 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1245 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1247 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1248 * only defers where it comes to the variable body
1249 * is not null. If no body is present compose->text
1250 * will be null in which case you cannot place the
1251 * cursor inside the component so. An empty component
1252 * is therefore created before placing the cursor
1254 case BODY_FIELD_PRESENT
:
1255 cursor_pos
= quote_fmt_get_cursor_pos();
1256 if (cursor_pos
== -1)
1257 gtk_widget_grab_focus(compose
->header_last
->entry
);
1259 gtk_widget_grab_focus(compose
->text
);
1263 undo_unblock(compose
->undostruct
);
1265 if (prefs_common
.auto_exteditor
)
1266 compose_exec_ext_editor(compose
);
1268 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1270 SCROLL_TO_CURSOR(compose
);
1272 compose
->modified
= FALSE
;
1273 compose_set_title(compose
);
1275 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1280 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1281 gboolean override_pref
, const gchar
*system
)
1283 const gchar
*privacy
= NULL
;
1285 cm_return_if_fail(compose
!= NULL
);
1286 cm_return_if_fail(account
!= NULL
);
1288 if (privacy_system_can_encrypt(compose
->privacy_system
) == FALSE
||
1289 (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
))
1292 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1293 privacy
= account
->default_privacy_system
;
1297 GSList
*privacy_avail
= privacy_get_system_ids();
1298 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1299 privacy
= (gchar
*)(privacy_avail
->data
);
1301 g_slist_free_full(privacy_avail
, g_free
);
1303 if (privacy
!= NULL
) {
1305 g_free(compose
->privacy_system
);
1306 compose
->privacy_system
= NULL
;
1307 g_free(compose
->encdata
);
1308 compose
->encdata
= NULL
;
1310 if (compose
->privacy_system
== NULL
)
1311 compose
->privacy_system
= g_strdup(privacy
);
1312 else if (*(compose
->privacy_system
) == '\0') {
1313 g_free(compose
->privacy_system
);
1314 g_free(compose
->encdata
);
1315 compose
->encdata
= NULL
;
1316 compose
->privacy_system
= g_strdup(privacy
);
1318 compose_update_privacy_system_menu_item(compose
, FALSE
);
1319 compose_use_encryption(compose
, TRUE
);
1323 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1325 const gchar
*privacy
= NULL
;
1326 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
)
1329 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1330 privacy
= account
->default_privacy_system
;
1334 GSList
*privacy_avail
= privacy_get_system_ids();
1335 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1336 privacy
= (gchar
*)(privacy_avail
->data
);
1340 if (privacy
!= NULL
) {
1342 g_free(compose
->privacy_system
);
1343 compose
->privacy_system
= NULL
;
1344 g_free(compose
->encdata
);
1345 compose
->encdata
= NULL
;
1347 if (compose
->privacy_system
== NULL
)
1348 compose
->privacy_system
= g_strdup(privacy
);
1349 compose_update_privacy_system_menu_item(compose
, FALSE
);
1350 compose_use_signing(compose
, TRUE
);
1354 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1358 Compose
*compose
= NULL
;
1360 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1362 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1363 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1365 list_len
= g_slist_length(msginfo_list
);
1369 case COMPOSE_REPLY_TO_ADDRESS
:
1370 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1371 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1373 case COMPOSE_REPLY_WITH_QUOTE
:
1374 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1375 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1377 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1378 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1379 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1381 case COMPOSE_REPLY_TO_SENDER
:
1382 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1383 FALSE
, FALSE
, TRUE
, body
);
1385 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1386 compose
= compose_followup_and_reply_to(msginfo
,
1387 COMPOSE_QUOTE_CHECK
,
1388 FALSE
, FALSE
, body
);
1390 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1391 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1392 FALSE
, FALSE
, TRUE
, body
);
1394 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1395 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1396 FALSE
, FALSE
, TRUE
, NULL
);
1398 case COMPOSE_REPLY_TO_ALL
:
1399 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1400 TRUE
, FALSE
, FALSE
, body
);
1402 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1403 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1404 TRUE
, FALSE
, FALSE
, body
);
1406 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1407 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1408 TRUE
, FALSE
, FALSE
, NULL
);
1410 case COMPOSE_REPLY_TO_LIST
:
1411 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1412 FALSE
, TRUE
, FALSE
, body
);
1414 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1415 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1416 FALSE
, TRUE
, FALSE
, body
);
1418 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1419 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1420 FALSE
, TRUE
, FALSE
, NULL
);
1422 case COMPOSE_FORWARD
:
1423 if (prefs_common
.forward_as_attachment
) {
1424 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1427 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1431 case COMPOSE_FORWARD_INLINE
:
1432 /* check if we reply to more than one Message */
1433 if (list_len
== 1) {
1434 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1437 /* more messages FALL THROUGH */
1438 case COMPOSE_FORWARD_AS_ATTACH
:
1439 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1441 case COMPOSE_REDIRECT
:
1442 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1445 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1448 if (compose
== NULL
) {
1449 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1453 compose
->rmode
= mode
;
1454 switch (compose
->rmode
) {
1456 case COMPOSE_REPLY_WITH_QUOTE
:
1457 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1458 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1459 debug_print("reply mode Normal\n");
1460 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1461 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1463 case COMPOSE_REPLY_TO_SENDER
:
1464 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1465 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1466 debug_print("reply mode Sender\n");
1467 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1469 case COMPOSE_REPLY_TO_ALL
:
1470 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1471 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1472 debug_print("reply mode All\n");
1473 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1475 case COMPOSE_REPLY_TO_LIST
:
1476 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1477 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1478 debug_print("reply mode List\n");
1479 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1481 case COMPOSE_REPLY_TO_ADDRESS
:
1482 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1490 static Compose
*compose_reply(MsgInfo
*msginfo
,
1491 ComposeQuoteMode quote_mode
,
1497 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1498 to_sender
, FALSE
, body
);
1501 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1502 ComposeQuoteMode quote_mode
,
1507 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1508 to_sender
, TRUE
, body
);
1511 static void compose_extract_original_charset(Compose
*compose
)
1513 MsgInfo
*info
= NULL
;
1514 if (compose
->replyinfo
) {
1515 info
= compose
->replyinfo
;
1516 } else if (compose
->fwdinfo
) {
1517 info
= compose
->fwdinfo
;
1518 } else if (compose
->targetinfo
) {
1519 info
= compose
->targetinfo
;
1522 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1523 MimeInfo
*partinfo
= mimeinfo
;
1524 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1525 partinfo
= procmime_mimeinfo_next(partinfo
);
1527 compose
->orig_charset
=
1528 g_strdup(procmime_mimeinfo_get_parameter(
1529 partinfo
, "charset"));
1531 procmime_mimeinfo_free_all(&mimeinfo
);
1535 #define SIGNAL_BLOCK(buffer) { \
1536 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1537 G_CALLBACK(compose_changed_cb), \
1539 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1540 G_CALLBACK(text_inserted), \
1544 #define SIGNAL_UNBLOCK(buffer) { \
1545 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1546 G_CALLBACK(compose_changed_cb), \
1548 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1549 G_CALLBACK(text_inserted), \
1553 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1554 ComposeQuoteMode quote_mode
,
1555 gboolean to_all
, gboolean to_ml
,
1557 gboolean followup_and_reply_to
,
1561 PrefsAccount
*account
= NULL
;
1562 GtkTextView
*textview
;
1563 GtkTextBuffer
*textbuf
;
1564 gboolean quote
= FALSE
;
1565 const gchar
*qmark
= NULL
;
1566 const gchar
*body_fmt
= NULL
;
1567 gchar
*s_system
= NULL
;
1569 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1570 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1572 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1574 cm_return_val_if_fail(account
!= NULL
, NULL
);
1576 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1577 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1579 compose
->updating
= TRUE
;
1581 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1582 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1584 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1585 if (!compose
->replyinfo
)
1586 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1588 compose_extract_original_charset(compose
);
1590 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1591 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1593 /* Set save folder */
1594 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1595 gchar
*folderidentifier
;
1597 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1598 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1599 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1600 compose_set_save_to(compose
, folderidentifier
);
1601 g_free(folderidentifier
);
1604 if (compose_parse_header(compose
, msginfo
) < 0) {
1605 compose
->updating
= FALSE
;
1606 compose_destroy(compose
);
1610 /* override from name according to folder properties */
1611 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1612 msginfo
->folder
->prefs
->reply_with_format
&&
1613 msginfo
->folder
->prefs
->reply_override_from_format
&&
1614 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1619 /* decode \-escape sequences in the internal representation of the quote format */
1620 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1621 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1624 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1625 compose
->gtkaspell
);
1627 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1629 quote_fmt_scan_string(tmp
);
1632 buf
= quote_fmt_get_buffer();
1634 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1636 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1637 quote_fmt_reset_vartable();
1638 quote_fmtlex_destroy();
1643 textview
= (GTK_TEXT_VIEW(compose
->text
));
1644 textbuf
= gtk_text_view_get_buffer(textview
);
1645 compose_create_tags(textview
, compose
);
1647 undo_block(compose
->undostruct
);
1649 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1650 gtkaspell_block_check(compose
->gtkaspell
);
1653 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1654 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1655 /* use the reply format of folder (if enabled), or the account's one
1656 (if enabled) or fallback to the global reply format, which is always
1657 enabled (even if empty), and use the relevant quotemark */
1659 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1660 msginfo
->folder
->prefs
->reply_with_format
) {
1661 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1662 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1664 } else if (account
->reply_with_format
) {
1665 qmark
= account
->reply_quotemark
;
1666 body_fmt
= account
->reply_body_format
;
1669 qmark
= prefs_common
.quotemark
;
1670 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1671 body_fmt
= gettext(prefs_common
.quotefmt
);
1678 /* empty quotemark is not allowed */
1679 if (qmark
== NULL
|| *qmark
== '\0')
1681 compose_quote_fmt(compose
, compose
->replyinfo
,
1682 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1683 _("The body of the \"Reply\" template has an error at line %d."));
1684 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1685 quote_fmt_reset_vartable();
1688 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1689 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1692 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1693 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1694 compose_force_signing(compose
, account
, s_system
);
1698 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1699 ((account
->default_encrypt
|| account
->default_sign
) ||
1700 (account
->default_encrypt_reply
&& MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) ||
1701 (account
->default_sign_reply
&& MSG_IS_SIGNED(compose
->replyinfo
->flags
))))
1702 COMPOSE_PRIVACY_WARNING();
1704 SIGNAL_BLOCK(textbuf
);
1706 if (account
->auto_sig
)
1707 compose_insert_sig(compose
, FALSE
);
1709 compose_wrap_all(compose
);
1712 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1713 gtkaspell_highlight_all(compose
->gtkaspell
);
1714 gtkaspell_unblock_check(compose
->gtkaspell
);
1716 SIGNAL_UNBLOCK(textbuf
);
1718 gtk_widget_grab_focus(compose
->text
);
1720 undo_unblock(compose
->undostruct
);
1722 if (prefs_common
.auto_exteditor
)
1723 compose_exec_ext_editor(compose
);
1725 compose
->modified
= FALSE
;
1726 compose_set_title(compose
);
1728 compose
->updating
= FALSE
;
1729 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1730 SCROLL_TO_CURSOR(compose
);
1732 if (compose
->deferred_destroy
) {
1733 compose_destroy(compose
);
1741 #define INSERT_FW_HEADER(var, hdr) \
1742 if (msginfo->var && *msginfo->var) { \
1743 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1744 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1745 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1748 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1749 gboolean as_attach
, const gchar
*body
,
1750 gboolean no_extedit
,
1754 GtkTextView
*textview
;
1755 GtkTextBuffer
*textbuf
;
1756 gint cursor_pos
= -1;
1759 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1760 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1762 if (!account
&& !(account
= compose_find_account(msginfo
)))
1763 account
= cur_account
;
1765 if (!prefs_common
.forward_as_attachment
)
1766 mode
= COMPOSE_FORWARD_INLINE
;
1768 mode
= COMPOSE_FORWARD
;
1769 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1770 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1772 compose
->updating
= TRUE
;
1773 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1774 if (!compose
->fwdinfo
)
1775 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1777 compose_extract_original_charset(compose
);
1779 if (msginfo
->subject
&& *msginfo
->subject
) {
1780 gchar
*buf
, *buf2
, *p
;
1782 buf
= p
= g_strdup(msginfo
->subject
);
1783 p
+= subject_get_prefix_length(p
);
1784 memmove(buf
, p
, strlen(p
) + 1);
1786 buf2
= g_strdup_printf("Fw: %s", buf
);
1787 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1793 /* override from name according to folder properties */
1794 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1795 msginfo
->folder
->prefs
->forward_with_format
&&
1796 msginfo
->folder
->prefs
->forward_override_from_format
&&
1797 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1801 MsgInfo
*full_msginfo
= NULL
;
1804 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1806 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1808 /* decode \-escape sequences in the internal representation of the quote format */
1809 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1810 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1813 gtkaspell_block_check(compose
->gtkaspell
);
1814 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1815 compose
->gtkaspell
);
1817 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1819 quote_fmt_scan_string(tmp
);
1822 buf
= quote_fmt_get_buffer();
1824 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1826 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1827 quote_fmt_reset_vartable();
1828 quote_fmtlex_destroy();
1831 procmsg_msginfo_free(&full_msginfo
);
1834 textview
= GTK_TEXT_VIEW(compose
->text
);
1835 textbuf
= gtk_text_view_get_buffer(textview
);
1836 compose_create_tags(textview
, compose
);
1838 undo_block(compose
->undostruct
);
1842 msgfile
= procmsg_get_message_file(msginfo
);
1843 if (!is_file_exist(msgfile
))
1844 g_warning("%s: file does not exist", msgfile
);
1846 compose_attach_append(compose
, msgfile
, msgfile
,
1847 "message/rfc822", NULL
);
1851 const gchar
*qmark
= NULL
;
1852 const gchar
*body_fmt
= NULL
;
1853 MsgInfo
*full_msginfo
;
1855 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1857 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1859 /* use the forward format of folder (if enabled), or the account's one
1860 (if enabled) or fallback to the global forward format, which is always
1861 enabled (even if empty), and use the relevant quotemark */
1862 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1863 msginfo
->folder
->prefs
->forward_with_format
) {
1864 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1865 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1867 } else if (account
->forward_with_format
) {
1868 qmark
= account
->forward_quotemark
;
1869 body_fmt
= account
->forward_body_format
;
1872 qmark
= prefs_common
.fw_quotemark
;
1873 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1874 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1879 /* empty quotemark is not allowed */
1880 if (qmark
== NULL
|| *qmark
== '\0')
1883 compose_quote_fmt(compose
, full_msginfo
,
1884 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1885 _("The body of the \"Forward\" template has an error at line %d."));
1886 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1887 quote_fmt_reset_vartable();
1888 compose_attach_parts(compose
, msginfo
);
1890 procmsg_msginfo_free(&full_msginfo
);
1893 SIGNAL_BLOCK(textbuf
);
1895 if (account
->auto_sig
)
1896 compose_insert_sig(compose
, FALSE
);
1898 compose_wrap_all(compose
);
1901 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1902 gtkaspell_highlight_all(compose
->gtkaspell
);
1903 gtkaspell_unblock_check(compose
->gtkaspell
);
1905 SIGNAL_UNBLOCK(textbuf
);
1907 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1908 (account
->default_encrypt
|| account
->default_sign
))
1909 COMPOSE_PRIVACY_WARNING();
1911 cursor_pos
= quote_fmt_get_cursor_pos();
1912 if (cursor_pos
== -1)
1913 gtk_widget_grab_focus(compose
->header_last
->entry
);
1915 gtk_widget_grab_focus(compose
->text
);
1917 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1918 compose_exec_ext_editor(compose
);
1921 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1922 gchar
*folderidentifier
;
1924 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1925 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1926 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1927 compose_set_save_to(compose
, folderidentifier
);
1928 g_free(folderidentifier
);
1931 undo_unblock(compose
->undostruct
);
1933 compose
->modified
= FALSE
;
1934 compose_set_title(compose
);
1936 compose
->updating
= FALSE
;
1937 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1938 SCROLL_TO_CURSOR(compose
);
1940 if (compose
->deferred_destroy
) {
1941 compose_destroy(compose
);
1945 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1950 #undef INSERT_FW_HEADER
1952 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1955 GtkTextView
*textview
;
1956 GtkTextBuffer
*textbuf
;
1960 gboolean single_mail
= TRUE
;
1962 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1964 if (g_slist_length(msginfo_list
) > 1)
1965 single_mail
= FALSE
;
1967 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1968 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1971 /* guess account from first selected message */
1973 !(account
= compose_find_account(msginfo_list
->data
)))
1974 account
= cur_account
;
1976 cm_return_val_if_fail(account
!= NULL
, NULL
);
1978 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1979 if (msginfo
->data
) {
1980 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1981 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1985 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1986 g_warning("no msginfo_list");
1990 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1991 compose_apply_folder_privacy_settings(compose
, ((MsgInfo
*)msginfo_list
->data
)->folder
);
1992 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1993 (account
->default_encrypt
|| account
->default_sign
))
1994 COMPOSE_PRIVACY_WARNING();
1996 compose
->updating
= TRUE
;
1998 /* override from name according to folder properties */
1999 if (msginfo_list
->data
) {
2000 MsgInfo
*msginfo
= msginfo_list
->data
;
2002 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
2003 msginfo
->folder
->prefs
->forward_with_format
&&
2004 msginfo
->folder
->prefs
->forward_override_from_format
&&
2005 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
2010 /* decode \-escape sequences in the internal representation of the quote format */
2011 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
2012 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2015 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2016 compose
->gtkaspell
);
2018 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2020 quote_fmt_scan_string(tmp
);
2023 buf
= quote_fmt_get_buffer();
2025 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2027 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2028 quote_fmt_reset_vartable();
2029 quote_fmtlex_destroy();
2035 textview
= GTK_TEXT_VIEW(compose
->text
);
2036 textbuf
= gtk_text_view_get_buffer(textview
);
2037 compose_create_tags(textview
, compose
);
2039 undo_block(compose
->undostruct
);
2040 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2041 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2043 if (!is_file_exist(msgfile
))
2044 g_warning("%s: file does not exist", msgfile
);
2046 compose_attach_append(compose
, msgfile
, msgfile
,
2047 "message/rfc822", NULL
);
2052 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2053 if (info
->subject
&& *info
->subject
) {
2054 gchar
*buf
, *buf2
, *p
;
2056 buf
= p
= g_strdup(info
->subject
);
2057 p
+= subject_get_prefix_length(p
);
2058 memmove(buf
, p
, strlen(p
) + 1);
2060 buf2
= g_strdup_printf("Fw: %s", buf
);
2061 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2067 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2068 _("Fw: multiple emails"));
2071 SIGNAL_BLOCK(textbuf
);
2073 if (account
->auto_sig
)
2074 compose_insert_sig(compose
, FALSE
);
2076 compose_wrap_all(compose
);
2078 SIGNAL_UNBLOCK(textbuf
);
2080 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2081 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2083 if (prefs_common
.auto_exteditor
)
2084 compose_exec_ext_editor(compose
);
2086 gtk_widget_grab_focus(compose
->header_last
->entry
);
2087 undo_unblock(compose
->undostruct
);
2088 compose
->modified
= FALSE
;
2089 compose_set_title(compose
);
2091 compose
->updating
= FALSE
;
2092 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2093 SCROLL_TO_CURSOR(compose
);
2095 if (compose
->deferred_destroy
) {
2096 compose_destroy(compose
);
2100 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2105 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2107 GtkTextIter start
= *iter
;
2108 GtkTextIter end_iter
;
2109 int start_pos
= gtk_text_iter_get_offset(&start
);
2111 if (!compose
->account
->sig_sep
)
2114 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2115 start_pos
+strlen(compose
->account
->sig_sep
));
2117 /* check sig separator */
2118 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2119 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2121 /* check end of line (\n) */
2122 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2123 start_pos
+strlen(compose
->account
->sig_sep
));
2124 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2125 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2126 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2127 if (!strcmp(tmp
,"\n")) {
2139 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2141 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2142 Compose
*compose
= (Compose
*)data
;
2143 FolderItem
*old_item
= NULL
;
2144 FolderItem
*new_item
= NULL
;
2145 gchar
*old_id
, *new_id
;
2147 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2148 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2151 old_item
= hookdata
->item
;
2152 new_item
= hookdata
->item2
;
2154 old_id
= folder_item_get_identifier(old_item
);
2155 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2157 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2158 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2159 compose
->targetinfo
->folder
= new_item
;
2162 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2163 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2164 compose
->replyinfo
->folder
= new_item
;
2167 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2168 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2169 compose
->fwdinfo
->folder
= new_item
;
2177 static void compose_colorize_signature(Compose
*compose
)
2179 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2181 GtkTextIter end_iter
;
2182 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2183 while (gtk_text_iter_forward_line(&iter
))
2184 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2185 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2186 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2190 #define BLOCK_WRAP() { \
2191 prev_autowrap = compose->autowrap; \
2192 buffer = gtk_text_view_get_buffer( \
2193 GTK_TEXT_VIEW(compose->text)); \
2194 compose->autowrap = FALSE; \
2196 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2197 G_CALLBACK(compose_changed_cb), \
2199 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2200 G_CALLBACK(text_inserted), \
2203 #define UNBLOCK_WRAP() { \
2204 compose->autowrap = prev_autowrap; \
2205 if (compose->autowrap) { \
2206 gint old = compose->draft_timeout_tag; \
2207 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2208 compose_wrap_all(compose); \
2209 compose->draft_timeout_tag = old; \
2212 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2213 G_CALLBACK(compose_changed_cb), \
2215 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2216 G_CALLBACK(text_inserted), \
2220 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2222 Compose
*compose
= NULL
;
2223 PrefsAccount
*account
= NULL
;
2224 GtkTextView
*textview
;
2225 GtkTextBuffer
*textbuf
;
2229 gboolean use_signing
= FALSE
;
2230 gboolean use_encryption
= FALSE
;
2231 gchar
*privacy_system
= NULL
;
2232 int priority
= PRIORITY_NORMAL
;
2233 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2234 gboolean autowrap
= prefs_common
.autowrap
;
2235 gboolean autoindent
= prefs_common
.auto_indent
;
2236 HeaderEntry
*manual_headers
= NULL
;
2238 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2239 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2241 if (compose_put_existing_to_front(msginfo
)) {
2245 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2246 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2247 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2248 gchar
*queueheader_buf
= NULL
;
2251 /* Select Account from queue headers */
2252 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2253 "X-Claws-Account-Id:")) {
2254 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2255 account
= account_find_from_id(id
);
2256 g_free(queueheader_buf
);
2258 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2259 "X-Sylpheed-Account-Id:")) {
2260 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2261 account
= account_find_from_id(id
);
2262 g_free(queueheader_buf
);
2264 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2266 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2267 account
= account_find_from_id(id
);
2268 g_free(queueheader_buf
);
2270 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2272 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2273 account
= account_find_from_id(id
);
2274 g_free(queueheader_buf
);
2276 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2278 account
= account_find_from_address(queueheader_buf
, FALSE
);
2279 g_free(queueheader_buf
);
2281 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2283 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2284 use_signing
= param
;
2285 g_free(queueheader_buf
);
2287 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2288 "X-Sylpheed-Sign:")) {
2289 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2290 use_signing
= param
;
2291 g_free(queueheader_buf
);
2293 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2294 "X-Claws-Encrypt:")) {
2295 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2296 use_encryption
= param
;
2297 g_free(queueheader_buf
);
2299 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2300 "X-Sylpheed-Encrypt:")) {
2301 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2302 use_encryption
= param
;
2303 g_free(queueheader_buf
);
2305 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2306 "X-Claws-Auto-Wrapping:")) {
2307 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2309 g_free(queueheader_buf
);
2311 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2312 "X-Claws-Auto-Indent:")) {
2313 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2315 g_free(queueheader_buf
);
2317 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2318 "X-Claws-Privacy-System:")) {
2319 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2320 g_free(queueheader_buf
);
2322 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2323 "X-Sylpheed-Privacy-System:")) {
2324 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2325 g_free(queueheader_buf
);
2327 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2329 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2331 g_free(queueheader_buf
);
2333 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2335 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2336 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2337 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2338 if (orig_item
!= NULL
) {
2339 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2343 g_free(queueheader_buf
);
2345 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2347 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2348 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2349 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2350 if (orig_item
!= NULL
) {
2351 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2355 g_free(queueheader_buf
);
2357 /* Get manual headers */
2358 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2359 "X-Claws-Manual-Headers:")) {
2360 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2361 if (listmh
&& *listmh
!= '\0') {
2362 debug_print("Got manual headers: %s\n", listmh
);
2363 manual_headers
= procheader_entries_from_str(listmh
);
2366 g_free(queueheader_buf
);
2369 account
= msginfo
->folder
->folder
->account
;
2372 if (!account
&& prefs_common
.reedit_account_autosel
) {
2374 if (!procheader_get_header_from_msginfo(msginfo
, &from
, "FROM:")) {
2375 extract_address(from
);
2376 account
= account_find_from_address(from
, FALSE
);
2381 account
= cur_account
;
2383 cm_return_val_if_fail(account
!= NULL
, NULL
);
2385 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2387 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
2388 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
2389 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2390 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2391 compose
->autowrap
= autowrap
;
2392 compose
->replyinfo
= replyinfo
;
2393 compose
->fwdinfo
= fwdinfo
;
2395 compose
->updating
= TRUE
;
2396 compose
->priority
= priority
;
2398 if (privacy_system
!= NULL
) {
2399 compose
->privacy_system
= privacy_system
;
2400 compose_use_signing(compose
, use_signing
);
2401 compose_use_encryption(compose
, use_encryption
);
2402 compose_update_privacy_system_menu_item(compose
, FALSE
);
2404 compose_activate_privacy_system(compose
, account
, FALSE
);
2406 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2407 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
2408 (account
->default_encrypt
|| account
->default_sign
))
2409 COMPOSE_PRIVACY_WARNING();
2411 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2412 compose
->targetinfo
->tags
= g_slist_copy(msginfo
->tags
);
2414 compose_extract_original_charset(compose
);
2416 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2417 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2418 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2419 gchar
*queueheader_buf
= NULL
;
2421 /* Set message save folder */
2422 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "SCF:")) {
2423 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2424 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2425 compose_set_save_to(compose
, &queueheader_buf
[4]);
2426 g_free(queueheader_buf
);
2428 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "RRCPT:")) {
2429 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2431 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2433 g_free(queueheader_buf
);
2437 if (compose_parse_header(compose
, msginfo
) < 0) {
2438 compose
->updating
= FALSE
;
2439 compose_destroy(compose
);
2442 compose_reedit_set_entry(compose
, msginfo
);
2444 textview
= GTK_TEXT_VIEW(compose
->text
);
2445 textbuf
= gtk_text_view_get_buffer(textview
);
2446 compose_create_tags(textview
, compose
);
2448 mark
= gtk_text_buffer_get_insert(textbuf
);
2449 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2451 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2452 G_CALLBACK(compose_changed_cb
),
2455 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2456 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2458 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2461 fp
= procmime_get_first_text_content(msginfo
);
2464 g_warning("Can't get text part");
2468 gchar buf
[BUFFSIZE
];
2469 gboolean prev_autowrap
;
2470 GtkTextBuffer
*buffer
;
2472 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2474 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2480 compose_attach_parts(compose
, msginfo
);
2482 compose_colorize_signature(compose
);
2484 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2485 G_CALLBACK(compose_changed_cb
),
2488 if (manual_headers
!= NULL
) {
2489 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2490 procheader_entries_free(manual_headers
);
2491 compose
->updating
= FALSE
;
2492 compose_destroy(compose
);
2495 procheader_entries_free(manual_headers
);
2498 gtk_widget_grab_focus(compose
->text
);
2500 if (prefs_common
.auto_exteditor
) {
2501 compose_exec_ext_editor(compose
);
2503 compose
->modified
= FALSE
;
2504 compose_set_title(compose
);
2506 compose
->updating
= FALSE
;
2507 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2508 SCROLL_TO_CURSOR(compose
);
2510 if (compose
->deferred_destroy
) {
2511 compose_destroy(compose
);
2515 compose
->sig_str
= account_get_signature_str(compose
->account
);
2517 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2522 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2529 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2532 account
= account_get_reply_account(msginfo
,
2533 prefs_common
.reply_account_autosel
);
2534 cm_return_val_if_fail(account
!= NULL
, NULL
);
2536 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2538 compose
->updating
= TRUE
;
2540 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2541 compose
->replyinfo
= NULL
;
2542 compose
->fwdinfo
= NULL
;
2544 compose_show_first_last_header(compose
, TRUE
);
2546 gtk_widget_grab_focus(compose
->header_last
->entry
);
2548 filename
= procmsg_get_message_file(msginfo
);
2550 if (filename
== NULL
) {
2551 compose
->updating
= FALSE
;
2552 compose_destroy(compose
);
2557 compose
->redirect_filename
= filename
;
2559 /* Set save folder */
2560 item
= msginfo
->folder
;
2561 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2562 gchar
*folderidentifier
;
2564 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2565 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2566 folderidentifier
= folder_item_get_identifier(item
);
2567 compose_set_save_to(compose
, folderidentifier
);
2568 g_free(folderidentifier
);
2571 compose_attach_parts(compose
, msginfo
);
2573 if (msginfo
->subject
)
2574 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2576 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2578 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2579 _("The body of the \"Redirect\" template has an error at line %d."));
2580 quote_fmt_reset_vartable();
2581 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2583 compose_colorize_signature(compose
);
2586 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2587 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2588 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2590 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2592 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2593 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2595 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2596 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2597 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2598 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2600 if (compose
->toolbar
->draft_btn
)
2601 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2602 if (compose
->toolbar
->insert_btn
)
2603 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2604 if (compose
->toolbar
->attach_btn
)
2605 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2606 if (compose
->toolbar
->sig_btn
)
2607 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2608 if (compose
->toolbar
->exteditor_btn
)
2609 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2610 if (compose
->toolbar
->linewrap_current_btn
)
2611 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2612 if (compose
->toolbar
->linewrap_all_btn
)
2613 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2614 if (compose
->toolbar
->privacy_sign_btn
)
2615 gtk_widget_set_sensitive(compose
->toolbar
->privacy_sign_btn
, FALSE
);
2616 if (compose
->toolbar
->privacy_encrypt_btn
)
2617 gtk_widget_set_sensitive(compose
->toolbar
->privacy_encrypt_btn
, FALSE
);
2619 compose
->modified
= FALSE
;
2620 compose_set_title(compose
);
2621 compose
->updating
= FALSE
;
2622 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2623 SCROLL_TO_CURSOR(compose
);
2625 if (compose
->deferred_destroy
) {
2626 compose_destroy(compose
);
2630 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2635 const GList
*compose_get_compose_list(void)
2637 return compose_list
;
2640 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2641 ComposeEntryType type
, ComposePrefType pref_type
)
2643 const gchar
*header
;
2645 gboolean in_quote
= FALSE
;
2646 if (!address
|| *address
== '\0') return;
2653 header
= N_("Bcc:");
2655 case COMPOSE_REPLYTO
:
2656 header
= N_("Reply-To:");
2658 case COMPOSE_NEWSGROUPS
:
2659 header
= N_("Newsgroups:");
2661 case COMPOSE_FOLLOWUPTO
:
2662 header
= N_( "Followup-To:");
2664 case COMPOSE_INREPLYTO
:
2665 header
= N_( "In-Reply-To:");
2672 header
= prefs_common_translated_header_name(header
);
2674 cur
= begin
= (gchar
*)address
;
2676 /* we separate the line by commas, but not if we're inside a quoted
2678 while (*cur
!= '\0') {
2680 in_quote
= !in_quote
;
2681 if (*cur
== ',' && !in_quote
) {
2682 gchar
*tmp
= g_strdup(begin
);
2684 tmp
[cur
-begin
]='\0';
2687 while (*tmp
== ' ' || *tmp
== '\t')
2689 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2690 compose_entry_indicate(compose
, tmp
);
2697 gchar
*tmp
= g_strdup(begin
);
2699 tmp
[cur
-begin
]='\0';
2700 while (*tmp
== ' ' || *tmp
== '\t')
2702 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2703 compose_entry_indicate(compose
, tmp
);
2708 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2713 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2714 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2715 if (gtk_entry_get_text(entry
) &&
2716 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2717 gtk_widget_modify_base(
2718 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2719 GTK_STATE_NORMAL
, &default_header_bgcolor
);
2720 gtk_widget_modify_text(
2721 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2722 GTK_STATE_NORMAL
, &default_header_color
);
2727 void compose_toolbar_cb(gint action
, gpointer data
)
2729 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2730 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2732 cm_return_if_fail(compose
!= NULL
);
2736 compose_send_cb(NULL
, compose
);
2739 compose_send_later_cb(NULL
, compose
);
2742 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2745 compose_insert_file_cb(NULL
, compose
);
2748 compose_attach_cb(NULL
, compose
);
2751 compose_insert_sig(compose
, FALSE
);
2754 compose_insert_sig(compose
, TRUE
);
2757 compose_ext_editor_cb(NULL
, compose
);
2759 case A_LINEWRAP_CURRENT
:
2760 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2762 case A_LINEWRAP_ALL
:
2763 compose_wrap_all_full(compose
, TRUE
);
2766 compose_address_cb(NULL
, compose
);
2769 case A_CHECK_SPELLING
:
2770 compose_check_all(NULL
, compose
);
2773 case A_PRIVACY_SIGN
:
2775 case A_PRIVACY_ENCRYPT
:
2782 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2787 gchar
*subject
= NULL
;
2791 gchar
**attach
= NULL
;
2792 gchar
*inreplyto
= NULL
;
2793 MailField mfield
= NO_FIELD_PRESENT
;
2795 /* get mailto parts but skip from */
2796 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2799 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2800 mfield
= TO_FIELD_PRESENT
;
2803 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2805 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2807 if (!g_utf8_validate (subject
, -1, NULL
)) {
2808 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2809 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2812 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2814 mfield
= SUBJECT_FIELD_PRESENT
;
2817 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2818 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2821 gboolean prev_autowrap
= compose
->autowrap
;
2823 compose
->autowrap
= FALSE
;
2825 mark
= gtk_text_buffer_get_insert(buffer
);
2826 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2828 if (!g_utf8_validate (body
, -1, NULL
)) {
2829 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2830 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2833 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2835 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2837 compose
->autowrap
= prev_autowrap
;
2838 if (compose
->autowrap
)
2839 compose_wrap_all(compose
);
2840 mfield
= BODY_FIELD_PRESENT
;
2844 gint i
= 0, att
= 0;
2845 gchar
*warn_files
= NULL
;
2846 while (attach
[i
] != NULL
) {
2847 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2848 if (utf8_filename
) {
2849 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2850 gchar
*tmp
= g_strdup_printf("%s%s\n",
2851 warn_files
?warn_files
:"",
2857 g_free(utf8_filename
);
2859 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2864 alertpanel_notice(ngettext(
2865 "The following file has been attached: \n%s",
2866 "The following files have been attached: \n%s", att
), warn_files
);
2871 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2884 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2886 static HeaderEntry hentry
[] = {
2887 {"Reply-To:", NULL
, TRUE
},
2888 {"Cc:", NULL
, TRUE
},
2889 {"References:", NULL
, FALSE
},
2890 {"Bcc:", NULL
, TRUE
},
2891 {"Newsgroups:", NULL
, TRUE
},
2892 {"Followup-To:", NULL
, TRUE
},
2893 {"List-Post:", NULL
, FALSE
},
2894 {"X-Priority:", NULL
, FALSE
},
2895 {NULL
, NULL
, FALSE
}
2912 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2914 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2915 procheader_get_header_fields(fp
, hentry
);
2918 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2919 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2921 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2924 g_free(hentry
[H_REPLY_TO
].body
);
2925 hentry
[H_REPLY_TO
].body
= NULL
;
2927 if (hentry
[H_CC
].body
!= NULL
) {
2928 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2929 g_free(hentry
[H_CC
].body
);
2930 hentry
[H_CC
].body
= NULL
;
2932 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2933 if (compose
->mode
== COMPOSE_REEDIT
)
2934 compose
->references
= hentry
[H_REFERENCES
].body
;
2936 compose
->references
= compose_parse_references
2937 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2938 g_free(hentry
[H_REFERENCES
].body
);
2940 hentry
[H_REFERENCES
].body
= NULL
;
2942 if (hentry
[H_BCC
].body
!= NULL
) {
2943 if (compose
->mode
== COMPOSE_REEDIT
)
2945 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2946 g_free(hentry
[H_BCC
].body
);
2947 hentry
[H_BCC
].body
= NULL
;
2949 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2950 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2951 hentry
[H_NEWSGROUPS
].body
= NULL
;
2953 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2954 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2955 compose
->followup_to
=
2956 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2959 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2960 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2962 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2963 gchar
*to
= NULL
, *start
= NULL
;
2965 extract_address(hentry
[H_LIST_POST
].body
);
2966 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2967 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2969 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2970 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2973 g_free(compose
->ml_post
);
2974 compose
->ml_post
= to
;
2977 g_free(hentry
[H_LIST_POST
].body
);
2978 hentry
[H_LIST_POST
].body
= NULL
;
2981 /* CLAWS - X-Priority */
2982 if (compose
->mode
== COMPOSE_REEDIT
)
2983 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2986 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2987 g_free(hentry
[H_X_PRIORITY
].body
);
2989 hentry
[H_X_PRIORITY
].body
= NULL
;
2991 if (priority
< PRIORITY_HIGHEST
||
2992 priority
> PRIORITY_LOWEST
)
2993 priority
= PRIORITY_NORMAL
;
2995 compose
->priority
= priority
;
2998 if (compose
->mode
== COMPOSE_REEDIT
) {
2999 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3000 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
3002 if (msginfo
->msgid
&& *msginfo
->msgid
&&
3003 compose
->folder
!= NULL
&&
3004 compose
->folder
->stype
== F_DRAFT
)
3005 compose
->msgid
= g_strdup(msginfo
->msgid
);
3007 if (msginfo
->msgid
&& *msginfo
->msgid
)
3008 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
3010 if (!compose
->references
) {
3011 if (msginfo
->msgid
&& *msginfo
->msgid
) {
3012 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3013 compose
->references
=
3014 g_strdup_printf("<%s>\n\t<%s>",
3018 compose
->references
=
3019 g_strconcat("<", msginfo
->msgid
, ">",
3021 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3022 compose
->references
=
3023 g_strconcat("<", msginfo
->inreplyto
, ">",
3032 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3037 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3039 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3040 procheader_get_header_fields(fp
, entries
);
3044 while (he
!= NULL
&& he
->name
!= NULL
) {
3046 GtkListStore
*model
= NULL
;
3048 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3049 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3050 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3051 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3052 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3059 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3061 GSList
*ref_id_list
, *cur
;
3065 ref_id_list
= references_list_append(NULL
, ref
);
3066 if (!ref_id_list
) return NULL
;
3067 if (msgid
&& *msgid
)
3068 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3073 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3074 /* "<" + Message-ID + ">" + CR+LF+TAB */
3075 len
+= strlen((gchar
*)cur
->data
) + 5;
3077 if (len
> MAX_REFERENCES_LEN
) {
3078 /* remove second message-ID */
3079 if (ref_id_list
&& ref_id_list
->next
&&
3080 ref_id_list
->next
->next
) {
3081 g_free(ref_id_list
->next
->data
);
3082 ref_id_list
= g_slist_remove
3083 (ref_id_list
, ref_id_list
->next
->data
);
3085 slist_free_strings_full(ref_id_list
);
3092 new_ref
= g_string_new("");
3093 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3094 if (new_ref
->len
> 0)
3095 g_string_append(new_ref
, "\n\t");
3096 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3099 slist_free_strings_full(ref_id_list
);
3101 new_ref_str
= new_ref
->str
;
3102 g_string_free(new_ref
, FALSE
);
3107 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3108 const gchar
*fmt
, const gchar
*qmark
,
3109 const gchar
*body
, gboolean rewrap
,
3110 gboolean need_unescape
,
3111 const gchar
*err_msg
)
3113 MsgInfo
* dummyinfo
= NULL
;
3114 gchar
*quote_str
= NULL
;
3116 gboolean prev_autowrap
;
3117 const gchar
*trimmed_body
= body
;
3118 gint cursor_pos
= -1;
3119 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3120 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3125 SIGNAL_BLOCK(buffer
);
3128 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3129 msginfo
= dummyinfo
;
3132 if (qmark
!= NULL
) {
3134 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3135 compose
->gtkaspell
);
3137 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3139 quote_fmt_scan_string(qmark
);
3142 buf
= quote_fmt_get_buffer();
3145 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3147 Xstrdup_a(quote_str
, buf
, goto error
)
3150 if (fmt
&& *fmt
!= '\0') {
3153 while (*trimmed_body
== '\n')
3157 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3158 compose
->gtkaspell
);
3160 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3162 if (need_unescape
) {
3165 /* decode \-escape sequences in the internal representation of the quote format */
3166 tmp
= g_malloc(strlen(fmt
)+1);
3167 pref_get_unescaped_pref(tmp
, fmt
);
3168 quote_fmt_scan_string(tmp
);
3172 quote_fmt_scan_string(fmt
);
3176 buf
= quote_fmt_get_buffer();
3179 gint line
= quote_fmt_get_line();
3180 alertpanel_error(err_msg
, line
);
3188 prev_autowrap
= compose
->autowrap
;
3189 compose
->autowrap
= FALSE
;
3191 mark
= gtk_text_buffer_get_insert(buffer
);
3192 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3193 if (g_utf8_validate(buf
, -1, NULL
)) {
3194 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3196 gchar
*tmpout
= NULL
;
3197 tmpout
= conv_codeset_strdup
3198 (buf
, conv_get_locale_charset_str_no_utf8(),
3200 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3202 tmpout
= g_malloc(strlen(buf
)*2+1);
3203 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3205 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3209 cursor_pos
= quote_fmt_get_cursor_pos();
3210 if (cursor_pos
== -1)
3211 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3212 compose
->set_cursor_pos
= cursor_pos
;
3214 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3215 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3216 gtk_text_buffer_place_cursor(buffer
, &iter
);
3218 compose
->autowrap
= prev_autowrap
;
3219 if (compose
->autowrap
&& rewrap
)
3220 compose_wrap_all(compose
);
3227 SIGNAL_UNBLOCK(buffer
);
3229 procmsg_msginfo_free( &dummyinfo
);
3234 /* if ml_post is of type addr@host and from is of type
3235 * addr-anything@host, return TRUE
3237 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3239 gchar
*left_ml
= NULL
;
3240 gchar
*right_ml
= NULL
;
3241 gchar
*left_from
= NULL
;
3242 gchar
*right_from
= NULL
;
3243 gboolean result
= FALSE
;
3245 if (!ml_post
|| !from
)
3248 left_ml
= g_strdup(ml_post
);
3249 if (strstr(left_ml
, "@")) {
3250 right_ml
= strstr(left_ml
, "@")+1;
3251 *(strstr(left_ml
, "@")) = '\0';
3254 left_from
= g_strdup(from
);
3255 if (strstr(left_from
, "@")) {
3256 right_from
= strstr(left_from
, "@")+1;
3257 *(strstr(left_from
, "@")) = '\0';
3260 if (right_ml
&& right_from
3261 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3262 && !strcmp(right_from
, right_ml
)) {
3271 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3272 gboolean respect_default_to
)
3276 if (!folder
|| !folder
->prefs
)
3279 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3280 compose_entry_append(compose
, folder
->prefs
->default_to
,
3281 COMPOSE_TO
, PREF_FOLDER
);
3282 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3284 if (folder
->prefs
->enable_default_cc
) {
3285 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3286 COMPOSE_CC
, PREF_FOLDER
);
3287 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3289 if (folder
->prefs
->enable_default_bcc
) {
3290 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3291 COMPOSE_BCC
, PREF_FOLDER
);
3292 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3294 if (folder
->prefs
->enable_default_replyto
) {
3295 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3296 COMPOSE_REPLYTO
, PREF_FOLDER
);
3297 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3301 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3306 if (!compose
|| !msginfo
)
3309 if (msginfo
->subject
&& *msginfo
->subject
) {
3310 buf
= p
= g_strdup(msginfo
->subject
);
3311 p
+= subject_get_prefix_length(p
);
3312 memmove(buf
, p
, strlen(p
) + 1);
3314 buf2
= g_strdup_printf("Re: %s", buf
);
3315 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3320 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3323 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3324 gboolean to_all
, gboolean to_ml
,
3326 gboolean followup_and_reply_to
)
3328 GSList
*cc_list
= NULL
;
3331 gchar
*replyto
= NULL
;
3332 gchar
*ac_email
= NULL
;
3334 gboolean reply_to_ml
= FALSE
;
3335 gboolean default_reply_to
= FALSE
;
3337 cm_return_if_fail(compose
->account
!= NULL
);
3338 cm_return_if_fail(msginfo
!= NULL
);
3340 reply_to_ml
= to_ml
&& compose
->ml_post
;
3342 default_reply_to
= msginfo
->folder
&&
3343 msginfo
->folder
->prefs
->enable_default_reply_to
;
3345 if (compose
->account
->protocol
!= A_NNTP
) {
3346 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3348 if (reply_to_ml
&& !default_reply_to
) {
3350 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3353 /* normal answer to ml post with a reply-to */
3354 compose_entry_append(compose
,
3356 COMPOSE_TO
, PREF_ML
);
3357 if (compose
->replyto
)
3358 compose_entry_append(compose
,
3360 COMPOSE_CC
, PREF_ML
);
3362 /* answer to subscription confirmation */
3363 if (compose
->replyto
)
3364 compose_entry_append(compose
,
3366 COMPOSE_TO
, PREF_ML
);
3367 else if (msginfo
->from
)
3368 compose_entry_append(compose
,
3370 COMPOSE_TO
, PREF_ML
);
3373 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3374 compose_entry_append(compose
,
3375 msginfo
->folder
->prefs
->default_reply_to
,
3376 COMPOSE_TO
, PREF_FOLDER
);
3377 compose_entry_indicate(compose
,
3378 msginfo
->folder
->prefs
->default_reply_to
);
3384 compose_entry_append(compose
, msginfo
->from
,
3385 COMPOSE_TO
, PREF_NONE
);
3387 Xstrdup_a(tmp1
, msginfo
->from
, return);
3388 extract_address(tmp1
);
3389 compose_entry_append(compose
,
3390 (!account_find_from_address(tmp1
, FALSE
))
3393 COMPOSE_TO
, PREF_NONE
);
3394 if (compose
->replyto
)
3395 compose_entry_append(compose
,
3397 COMPOSE_CC
, PREF_NONE
);
3399 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3400 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3401 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3402 if (compose
->replyto
) {
3403 compose_entry_append(compose
,
3405 COMPOSE_TO
, PREF_NONE
);
3407 compose_entry_append(compose
,
3408 msginfo
->from
? msginfo
->from
: "",
3409 COMPOSE_TO
, PREF_NONE
);
3412 /* replying to own mail, use original recp */
3413 compose_entry_append(compose
,
3414 msginfo
->to
? msginfo
->to
: "",
3415 COMPOSE_TO
, PREF_NONE
);
3416 compose_entry_append(compose
,
3417 msginfo
->cc
? msginfo
->cc
: "",
3418 COMPOSE_CC
, PREF_NONE
);
3423 if (to_sender
|| (compose
->followup_to
&&
3424 !strncmp(compose
->followup_to
, "poster", 6)))
3425 compose_entry_append
3427 (compose
->replyto
? compose
->replyto
:
3428 msginfo
->from
? msginfo
->from
: ""),
3429 COMPOSE_TO
, PREF_NONE
);
3431 else if (followup_and_reply_to
|| to_all
) {
3432 compose_entry_append
3434 (compose
->replyto
? compose
->replyto
:
3435 msginfo
->from
? msginfo
->from
: ""),
3436 COMPOSE_TO
, PREF_NONE
);
3438 compose_entry_append
3440 compose
->followup_to
? compose
->followup_to
:
3441 compose
->newsgroups
? compose
->newsgroups
: "",
3442 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3444 compose_entry_append
3446 msginfo
->cc
? msginfo
->cc
: "",
3447 COMPOSE_CC
, PREF_NONE
);
3450 compose_entry_append
3452 compose
->followup_to
? compose
->followup_to
:
3453 compose
->newsgroups
? compose
->newsgroups
: "",
3454 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3456 compose_reply_set_subject(compose
, msginfo
);
3458 if (to_ml
&& compose
->ml_post
) return;
3459 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3461 if (compose
->replyto
) {
3462 Xstrdup_a(replyto
, compose
->replyto
, return);
3463 extract_address(replyto
);
3465 if (msginfo
->from
) {
3466 Xstrdup_a(from
, msginfo
->from
, return);
3467 extract_address(from
);
3470 if (replyto
&& from
)
3471 cc_list
= address_list_append_with_comments(cc_list
, from
);
3472 if (to_all
&& msginfo
->folder
&&
3473 msginfo
->folder
->prefs
->enable_default_reply_to
)
3474 cc_list
= address_list_append_with_comments(cc_list
,
3475 msginfo
->folder
->prefs
->default_reply_to
);
3476 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3477 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3479 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3482 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3483 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3484 extract_address(addr
);
3486 if (strcmp(ac_email
, addr
))
3487 compose_entry_append(compose
, (gchar
*)cur
->data
,
3488 COMPOSE_CC
, PREF_NONE
);
3490 debug_print("Cc address same as compose account's, ignoring\n");
3495 slist_free_strings_full(cc_list
);
3501 #define SET_ENTRY(entry, str) \
3504 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3507 #define SET_ADDRESS(type, str) \
3510 compose_entry_append(compose, str, type, PREF_NONE); \
3513 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3515 cm_return_if_fail(msginfo
!= NULL
);
3517 SET_ENTRY(subject_entry
, msginfo
->subject
);
3518 SET_ENTRY(from_name
, msginfo
->from
);
3519 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3520 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3521 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3522 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3523 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3524 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3526 compose_update_priority_menu_item(compose
);
3527 compose_update_privacy_system_menu_item(compose
, FALSE
);
3528 compose_show_first_last_header(compose
, TRUE
);
3534 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3536 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3537 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3539 GtkTextIter iter
, iter_end
;
3540 gint cur_pos
, ins_pos
;
3541 gboolean prev_autowrap
;
3542 gboolean found
= FALSE
;
3543 gboolean exists
= FALSE
;
3545 cm_return_if_fail(compose
->account
!= NULL
);
3549 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3550 G_CALLBACK(compose_changed_cb
),
3553 mark
= gtk_text_buffer_get_insert(buffer
);
3554 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3555 cur_pos
= gtk_text_iter_get_offset (&iter
);
3558 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3560 exists
= (compose
->sig_str
!= NULL
);
3563 GtkTextIter first_iter
, start_iter
, end_iter
;
3565 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3567 if (!exists
|| compose
->sig_str
[0] == '\0')
3570 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3571 compose
->signature_tag
);
3574 /* include previous \n\n */
3575 gtk_text_iter_backward_chars(&first_iter
, 1);
3576 start_iter
= first_iter
;
3577 end_iter
= first_iter
;
3579 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3580 compose
->signature_tag
);
3581 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3582 compose
->signature_tag
);
3584 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3590 g_free(compose
->sig_str
);
3591 compose
->sig_str
= account_get_signature_str(compose
->account
);
3593 cur_pos
= gtk_text_iter_get_offset(&iter
);
3595 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3596 g_free(compose
->sig_str
);
3597 compose
->sig_str
= NULL
;
3599 if (compose
->sig_inserted
== FALSE
)
3600 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3601 compose
->sig_inserted
= TRUE
;
3603 cur_pos
= gtk_text_iter_get_offset(&iter
);
3604 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3606 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3607 gtk_text_iter_forward_chars(&iter
, 1);
3608 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3609 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3611 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3612 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3615 /* put the cursor where it should be
3616 * either where the quote_fmt says, either where it was */
3617 if (compose
->set_cursor_pos
< 0)
3618 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3620 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3621 compose
->set_cursor_pos
);
3623 compose
->set_cursor_pos
= -1;
3624 gtk_text_buffer_place_cursor(buffer
, &iter
);
3625 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3626 G_CALLBACK(compose_changed_cb
),
3632 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3635 GtkTextBuffer
*buffer
;
3638 const gchar
*cur_encoding
;
3639 gchar buf
[BUFFSIZE
];
3642 gboolean prev_autowrap
;
3646 GError
*error
= NULL
;
3652 GString
*file_contents
= NULL
;
3653 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3655 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3657 /* get the size of the file we are about to insert */
3659 f
= g_file_new_for_path(file
);
3660 fi
= g_file_query_info(f
, "standard::size",
3661 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3663 if (error
!= NULL
) {
3664 g_warning(error
->message
);
3666 g_error_free(error
);
3670 ret
= g_stat(file
, &file_stat
);
3673 gchar
*shortfile
= g_path_get_basename(file
);
3674 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3676 return COMPOSE_INSERT_NO_FILE
;
3677 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3679 size
= g_file_info_get_size(fi
);
3683 size
= file_stat
.st_size
;
3686 /* ask user for confirmation if the file is large */
3687 if (prefs_common
.warn_large_insert_size
< 0 ||
3688 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3692 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3693 "in the message body. Are you sure you want to do that?"),
3694 to_human_readable(size
));
3695 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3696 _("_Insert"), NULL
, ALERTFOCUS_SECOND
, TRUE
,
3697 NULL
, ALERT_QUESTION
);
3700 /* do we ask for confirmation next time? */
3701 if (aval
& G_ALERTDISABLE
) {
3702 /* no confirmation next time, disable feature in preferences */
3703 aval
&= ~G_ALERTDISABLE
;
3704 prefs_common
.warn_large_insert
= FALSE
;
3707 /* abort file insertion if user canceled action */
3708 if (aval
!= G_ALERTALTERNATE
) {
3709 return COMPOSE_INSERT_NO_FILE
;
3715 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3716 FILE_OP_ERROR(file
, "claws_fopen");
3717 return COMPOSE_INSERT_READ_ERROR
;
3720 prev_autowrap
= compose
->autowrap
;
3721 compose
->autowrap
= FALSE
;
3723 text
= GTK_TEXT_VIEW(compose
->text
);
3724 buffer
= gtk_text_view_get_buffer(text
);
3725 mark
= gtk_text_buffer_get_insert(buffer
);
3726 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3728 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3729 G_CALLBACK(text_inserted
),
3732 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3734 file_contents
= g_string_new("");
3735 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3738 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3739 str
= g_strdup(buf
);
3741 codeconv_set_strict(TRUE
);
3742 str
= conv_codeset_strdup
3743 (buf
, cur_encoding
, CS_INTERNAL
);
3744 codeconv_set_strict(FALSE
);
3747 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3753 /* strip <CR> if DOS/Windows file,
3754 replace <CR> with <LF> if Macintosh file. */
3757 if (len
> 0 && str
[len
- 1] != '\n') {
3759 if (str
[len
] == '\r') str
[len
] = '\n';
3762 file_contents
= g_string_append(file_contents
, str
);
3766 if (result
== COMPOSE_INSERT_SUCCESS
) {
3767 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3769 compose_changed_cb(NULL
, compose
);
3770 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3771 G_CALLBACK(text_inserted
),
3773 compose
->autowrap
= prev_autowrap
;
3774 if (compose
->autowrap
)
3775 compose_wrap_all(compose
);
3778 g_string_free(file_contents
, TRUE
);
3784 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3785 const gchar
*filename
,
3786 const gchar
*content_type
,
3787 const gchar
*charset
)
3795 GtkListStore
*store
;
3797 gboolean has_binary
= FALSE
;
3799 if (!is_file_exist(file
)) {
3800 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3801 gboolean result
= FALSE
;
3802 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3803 result
= compose_attach_append(
3804 compose
, file_from_uri
,
3805 filename
, content_type
,
3808 g_free(file_from_uri
);
3811 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3814 if ((size
= get_file_size(file
)) < 0) {
3815 alertpanel_error("Can't get file size of %s\n", filename
);
3819 /* In batch mode, we allow 0-length files to be attached no questions asked */
3820 if (size
== 0 && !compose
->batch
) {
3821 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3822 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3823 GTK_STOCK_CANCEL
, _("_Attach anyway"), NULL
,
3824 ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3827 if (aval
!= G_ALERTALTERNATE
) {
3831 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3832 alertpanel_error(_("Can't read %s."), filename
);
3837 ainfo
= g_new0(AttachInfo
, 1);
3838 auto_ainfo
= g_auto_pointer_new_with_free
3839 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3840 ainfo
->file
= g_strdup(file
);
3843 ainfo
->content_type
= g_strdup(content_type
);
3844 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3846 MsgFlags flags
= {0, 0};
3848 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3849 ainfo
->encoding
= ENC_7BIT
;
3851 ainfo
->encoding
= ENC_8BIT
;
3853 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3854 if (msginfo
&& msginfo
->subject
)
3855 name
= g_strdup(msginfo
->subject
);
3857 name
= g_path_get_basename(filename
? filename
: file
);
3859 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3861 procmsg_msginfo_free(&msginfo
);
3863 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3864 ainfo
->charset
= g_strdup(charset
);
3865 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3867 ainfo
->encoding
= ENC_BASE64
;
3869 name
= g_path_get_basename(filename
? filename
: file
);
3870 ainfo
->name
= g_strdup(name
);
3874 ainfo
->content_type
= procmime_get_mime_type(file
);
3875 if (!ainfo
->content_type
) {
3876 ainfo
->content_type
=
3877 g_strdup("application/octet-stream");
3878 ainfo
->encoding
= ENC_BASE64
;
3879 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3881 procmime_get_encoding_for_text_file(file
, &has_binary
);
3883 ainfo
->encoding
= ENC_BASE64
;
3884 name
= g_path_get_basename(filename
? filename
: file
);
3885 ainfo
->name
= g_strdup(name
);
3889 if (ainfo
->name
!= NULL
3890 && !strcmp(ainfo
->name
, ".")) {
3891 g_free(ainfo
->name
);
3895 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3896 g_free(ainfo
->content_type
);
3897 ainfo
->content_type
= g_strdup("application/octet-stream");
3898 g_free(ainfo
->charset
);
3899 ainfo
->charset
= NULL
;
3902 ainfo
->size
= (goffset
)size
;
3903 size_text
= to_human_readable((goffset
)size
);
3905 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3906 (GTK_TREE_VIEW(compose
->attach_clist
)));
3908 gtk_list_store_append(store
, &iter
);
3909 gtk_list_store_set(store
, &iter
,
3910 COL_MIMETYPE
, ainfo
->content_type
,
3911 COL_SIZE
, size_text
,
3912 COL_NAME
, ainfo
->name
,
3913 COL_CHARSET
, ainfo
->charset
,
3915 COL_AUTODATA
, auto_ainfo
,
3918 g_auto_pointer_free(auto_ainfo
);
3919 compose_attach_update_label(compose
);
3923 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3925 compose
->use_signing
= use_signing
;
3926 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3929 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3931 compose
->use_encryption
= use_encryption
;
3932 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3935 #define NEXT_PART_NOT_CHILD(info) \
3937 node = info->node; \
3938 while (node->children) \
3939 node = g_node_last_child(node); \
3940 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3943 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3947 MimeInfo
*firsttext
= NULL
;
3948 MimeInfo
*encrypted
= NULL
;
3951 const gchar
*partname
= NULL
;
3953 mimeinfo
= procmime_scan_message(msginfo
);
3954 if (!mimeinfo
) return;
3956 if (mimeinfo
->node
->children
== NULL
) {
3957 procmime_mimeinfo_free_all(&mimeinfo
);
3961 /* find first content part */
3962 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3963 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3964 child
= (MimeInfo
*)child
->node
->children
->data
;
3967 if (child
->type
== MIMETYPE_TEXT
) {
3969 debug_print("First text part found\n");
3970 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3971 child
->type
== MIMETYPE_APPLICATION
&&
3972 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3973 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3976 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3977 while (child
!= NULL
) {
3980 if (child
== encrypted
) {
3981 /* skip this part of tree */
3982 NEXT_PART_NOT_CHILD(child
);
3986 if (child
->type
== MIMETYPE_MULTIPART
) {
3987 /* get the actual content */
3988 child
= procmime_mimeinfo_next(child
);
3992 if (child
== firsttext
) {
3993 child
= procmime_mimeinfo_next(child
);
3997 outfile
= procmime_get_tmp_file_name(child
);
3998 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3999 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err
));
4001 gchar
*content_type
;
4003 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
4005 /* if we meet a pgp signature, we don't attach it, but
4006 * we force signing. */
4007 if ((strcmp(content_type
, "application/pgp-signature") &&
4008 strcmp(content_type
, "application/pkcs7-signature") &&
4009 strcmp(content_type
, "application/x-pkcs7-signature"))
4010 || compose
->mode
== COMPOSE_REDIRECT
) {
4011 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
4012 if (partname
== NULL
)
4013 partname
= procmime_mimeinfo_get_parameter(child
, "name");
4014 if (partname
== NULL
)
4016 compose_attach_append(compose
, outfile
,
4017 partname
, content_type
,
4018 procmime_mimeinfo_get_parameter(child
, "charset"));
4020 compose_force_signing(compose
, compose
->account
, NULL
);
4022 g_free(content_type
);
4025 NEXT_PART_NOT_CHILD(child
);
4027 procmime_mimeinfo_free_all(&mimeinfo
);
4030 #undef NEXT_PART_NOT_CHILD
4035 WAIT_FOR_INDENT_CHAR
,
4036 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4039 /* return indent length, we allow:
4040 indent characters followed by indent characters or spaces/tabs,
4041 alphabets and numbers immediately followed by indent characters,
4042 and the repeating sequences of the above
4043 If quote ends with multiple spaces, only the first one is included. */
4044 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4045 const GtkTextIter
*start
, gint
*len
)
4047 GtkTextIter iter
= *start
;
4051 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4054 gint alnum_count
= 0;
4055 gint space_count
= 0;
4058 if (prefs_common
.quote_chars
== NULL
) {
4062 while (!gtk_text_iter_ends_line(&iter
)) {
4063 wc
= gtk_text_iter_get_char(&iter
);
4064 if (g_unichar_iswide(wc
))
4066 clen
= g_unichar_to_utf8(wc
, ch
);
4070 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4071 is_space
= g_unichar_isspace(wc
);
4073 if (state
== WAIT_FOR_INDENT_CHAR
) {
4074 if (!is_indent
&& !g_unichar_isalnum(wc
))
4077 quote_len
+= alnum_count
+ space_count
+ 1;
4078 alnum_count
= space_count
= 0;
4079 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4082 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4083 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4087 else if (is_indent
) {
4088 quote_len
+= alnum_count
+ space_count
+ 1;
4089 alnum_count
= space_count
= 0;
4092 state
= WAIT_FOR_INDENT_CHAR
;
4096 gtk_text_iter_forward_char(&iter
);
4099 if (quote_len
> 0 && space_count
> 0)
4105 if (quote_len
> 0) {
4107 gtk_text_iter_forward_chars(&iter
, quote_len
);
4108 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4114 /* return >0 if the line is itemized */
4115 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4116 const GtkTextIter
*start
)
4118 GtkTextIter iter
= *start
;
4123 if (gtk_text_iter_ends_line(&iter
))
4128 wc
= gtk_text_iter_get_char(&iter
);
4129 if (!g_unichar_isspace(wc
))
4131 gtk_text_iter_forward_char(&iter
);
4132 if (gtk_text_iter_ends_line(&iter
))
4136 clen
= g_unichar_to_utf8(wc
, ch
);
4137 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4139 wc
== 0x2022 || /* BULLET */
4140 wc
== 0x2023 || /* TRIANGULAR BULLET */
4141 wc
== 0x2043 || /* HYPHEN BULLET */
4142 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4143 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4144 wc
== 0x2219 || /* BULLET OPERATOR */
4145 wc
== 0x25d8 || /* INVERSE BULLET */
4146 wc
== 0x25e6 || /* WHITE BULLET */
4147 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4148 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4149 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4150 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4151 wc
== 0x29bf /* CIRCLED BULLET */
4155 gtk_text_iter_forward_char(&iter
);
4156 if (gtk_text_iter_ends_line(&iter
))
4158 wc
= gtk_text_iter_get_char(&iter
);
4159 if (g_unichar_isspace(wc
)) {
4165 /* return the string at the start of the itemization */
4166 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4167 const GtkTextIter
*start
)
4169 GtkTextIter iter
= *start
;
4172 GString
*item_chars
= g_string_new("");
4175 if (gtk_text_iter_ends_line(&iter
))
4180 wc
= gtk_text_iter_get_char(&iter
);
4181 if (!g_unichar_isspace(wc
))
4183 gtk_text_iter_forward_char(&iter
);
4184 if (gtk_text_iter_ends_line(&iter
))
4186 g_string_append_unichar(item_chars
, wc
);
4189 str
= item_chars
->str
;
4190 g_string_free(item_chars
, FALSE
);
4194 /* return the number of spaces at a line's start */
4195 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4196 const GtkTextIter
*start
)
4198 GtkTextIter iter
= *start
;
4201 if (gtk_text_iter_ends_line(&iter
))
4205 wc
= gtk_text_iter_get_char(&iter
);
4206 if (!g_unichar_isspace(wc
))
4209 gtk_text_iter_forward_char(&iter
);
4210 if (gtk_text_iter_ends_line(&iter
))
4214 gtk_text_iter_forward_char(&iter
);
4215 if (gtk_text_iter_ends_line(&iter
))
4220 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4221 const GtkTextIter
*start
,
4222 GtkTextIter
*break_pos
,
4226 GtkTextIter iter
= *start
, line_end
= *start
;
4227 PangoLogAttr
*attrs
;
4234 gboolean can_break
= FALSE
;
4235 gboolean do_break
= FALSE
;
4236 gboolean was_white
= FALSE
;
4237 gboolean prev_dont_break
= FALSE
;
4239 gtk_text_iter_forward_to_line_end(&line_end
);
4240 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4241 len
= g_utf8_strlen(str
, -1);
4245 g_warning("compose_get_line_break_pos: len = 0!");
4249 /* g_print("breaking line: %d: %s (len = %d)\n",
4250 gtk_text_iter_get_line(&iter), str, len); */
4252 attrs
= g_new(PangoLogAttr
, len
+ 1);
4254 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4258 /* skip quote and leading spaces */
4259 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4262 wc
= g_utf8_get_char(p
);
4263 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4265 if (g_unichar_iswide(wc
))
4267 else if (*p
== '\t')
4271 p
= g_utf8_next_char(p
);
4274 for (; *p
!= '\0' && i
< len
; i
++) {
4275 PangoLogAttr
*attr
= attrs
+ i
;
4276 gunichar wc
= g_utf8_get_char(p
);
4279 /* attr->is_line_break will be false for some characters that
4280 * we want to break a line before, like '/' or ':', so we
4281 * also allow breaking on any non-wide character. The
4282 * mentioned pango attribute is still useful to decide on
4283 * line breaks when wide characters are involved. */
4284 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4285 && can_break
&& was_white
&& !prev_dont_break
)
4288 was_white
= attr
->is_white
;
4290 /* don't wrap URI */
4291 if ((uri_len
= get_uri_len(p
)) > 0) {
4293 if (pos
> 0 && col
> max_col
) {
4303 if (g_unichar_iswide(wc
)) {
4305 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4307 } else if (*p
== '\t')
4311 if (pos
> 0 && col
> max_col
) {
4316 if (*p
== '-' || *p
== '/')
4317 prev_dont_break
= TRUE
;
4319 prev_dont_break
= FALSE
;
4321 p
= g_utf8_next_char(p
);
4325 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4330 *break_pos
= *start
;
4331 gtk_text_iter_set_line_offset(break_pos
, pos
);
4336 static gboolean
compose_join_next_line(Compose
*compose
,
4337 GtkTextBuffer
*buffer
,
4339 const gchar
*quote_str
)
4341 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4342 PangoLogAttr attrs
[3];
4344 gchar
*next_quote_str
;
4347 gboolean keep_cursor
= FALSE
;
4349 if (!gtk_text_iter_forward_line(&iter_
) ||
4350 gtk_text_iter_ends_line(&iter_
)) {
4353 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4355 if ((quote_str
|| next_quote_str
) &&
4356 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4357 g_free(next_quote_str
);
4360 g_free(next_quote_str
);
4363 if (quote_len
> 0) {
4364 gtk_text_iter_forward_chars(&end
, quote_len
);
4365 if (gtk_text_iter_ends_line(&end
)) {
4370 /* don't join itemized lines */
4371 if (compose_itemized_length(buffer
, &end
) > 0) {
4375 /* don't join signature separator */
4376 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4379 /* delete quote str */
4381 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4383 /* don't join line breaks put by the user */
4385 gtk_text_iter_backward_char(&cur
);
4386 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4387 gtk_text_iter_forward_char(&cur
);
4391 gtk_text_iter_forward_char(&cur
);
4392 /* delete linebreak and extra spaces */
4393 while (gtk_text_iter_backward_char(&cur
)) {
4394 wc1
= gtk_text_iter_get_char(&cur
);
4395 if (!g_unichar_isspace(wc1
))
4400 while (!gtk_text_iter_ends_line(&cur
)) {
4401 wc1
= gtk_text_iter_get_char(&cur
);
4402 if (!g_unichar_isspace(wc1
))
4404 gtk_text_iter_forward_char(&cur
);
4407 if (!gtk_text_iter_equal(&prev
, &next
)) {
4410 mark
= gtk_text_buffer_get_insert(buffer
);
4411 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4412 if (gtk_text_iter_equal(&prev
, &cur
))
4414 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4418 /* insert space if required */
4419 gtk_text_iter_backward_char(&prev
);
4420 wc1
= gtk_text_iter_get_char(&prev
);
4421 wc2
= gtk_text_iter_get_char(&next
);
4422 gtk_text_iter_forward_char(&next
);
4423 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4424 pango_default_break(str
, -1, NULL
, attrs
, 3);
4425 if (!attrs
[1].is_line_break
||
4426 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4427 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4429 gtk_text_iter_backward_char(&iter_
);
4430 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4439 #define ADD_TXT_POS(bp_, ep_, pti_) \
4440 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4441 last = last->next; \
4442 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4443 last->next = NULL; \
4445 g_warning("alloc error scanning URIs"); \
4448 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4450 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4451 GtkTextBuffer
*buffer
;
4452 GtkTextIter iter
, break_pos
, end_of_line
;
4453 gchar
*quote_str
= NULL
;
4455 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4456 gboolean prev_autowrap
= compose
->autowrap
;
4457 gint startq_offset
= -1, noq_offset
= -1;
4458 gint uri_start
= -1, uri_stop
= -1;
4459 gint nouri_start
= -1, nouri_stop
= -1;
4460 gint num_blocks
= 0;
4461 gint quotelevel
= -1;
4462 gboolean modified
= force
;
4463 gboolean removed
= FALSE
;
4464 gboolean modified_before_remove
= FALSE
;
4466 gboolean start
= TRUE
;
4467 gint itemized_len
= 0, rem_item_len
= 0;
4468 gchar
*itemized_chars
= NULL
;
4469 gboolean item_continuation
= FALSE
;
4474 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4478 compose
->autowrap
= FALSE
;
4480 buffer
= gtk_text_view_get_buffer(text
);
4481 undo_wrapping(compose
->undostruct
, TRUE
);
4486 mark
= gtk_text_buffer_get_insert(buffer
);
4487 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4491 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4492 if (gtk_text_iter_ends_line(&iter
)) {
4493 while (gtk_text_iter_ends_line(&iter
) &&
4494 gtk_text_iter_forward_line(&iter
))
4497 while (gtk_text_iter_backward_line(&iter
)) {
4498 if (gtk_text_iter_ends_line(&iter
)) {
4499 gtk_text_iter_forward_line(&iter
);
4505 /* move to line start */
4506 gtk_text_iter_set_line_offset(&iter
, 0);
4509 itemized_len
= compose_itemized_length(buffer
, &iter
);
4511 if (!itemized_len
) {
4512 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4513 item_continuation
= TRUE
;
4517 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4519 /* go until paragraph end (empty line) */
4520 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4521 gchar
*scanpos
= NULL
;
4522 /* parse table - in order of priority */
4524 const gchar
*needle
; /* token */
4526 /* token search function */
4527 gchar
*(*search
) (const gchar
*haystack
,
4528 const gchar
*needle
);
4529 /* part parsing function */
4530 gboolean (*parse
) (const gchar
*start
,
4531 const gchar
*scanpos
,
4535 /* part to URI function */
4536 gchar
*(*build_uri
) (const gchar
*bp
,
4540 static struct table parser
[] = {
4541 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4542 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4543 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4544 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4545 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4546 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4547 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4548 {"@", strcasestr
, get_email_part
, make_email_string
}
4550 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4551 gint last_index
= PARSE_ELEMS
;
4553 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4557 if (!prev_autowrap
&& num_blocks
== 0) {
4559 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4560 G_CALLBACK(text_inserted
),
4563 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4566 uri_start
= uri_stop
= -1;
4568 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4571 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4572 if (startq_offset
== -1)
4573 startq_offset
= gtk_text_iter_get_offset(&iter
);
4574 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4575 if (quotelevel
> 2) {
4576 /* recycle colors */
4577 if (prefs_common
.recycle_quote_colors
)
4586 if (startq_offset
== -1)
4587 noq_offset
= gtk_text_iter_get_offset(&iter
);
4591 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4594 if (gtk_text_iter_ends_line(&iter
)) {
4596 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4597 prefs_common
.linewrap_len
,
4599 GtkTextIter prev
, next
, cur
;
4600 if (prev_autowrap
!= FALSE
|| force
) {
4601 compose
->automatic_break
= TRUE
;
4603 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4604 compose
->automatic_break
= FALSE
;
4605 if (itemized_len
&& compose
->autoindent
) {
4606 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4607 if (!item_continuation
)
4608 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4610 } else if (quote_str
&& wrap_quote
) {
4611 compose
->automatic_break
= TRUE
;
4613 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4614 compose
->automatic_break
= FALSE
;
4615 if (itemized_len
&& compose
->autoindent
) {
4616 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4617 if (!item_continuation
)
4618 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4622 /* remove trailing spaces */
4624 rem_item_len
= itemized_len
;
4625 while (compose
->autoindent
&& rem_item_len
-- > 0)
4626 gtk_text_iter_backward_char(&cur
);
4627 gtk_text_iter_backward_char(&cur
);
4630 while (!gtk_text_iter_starts_line(&cur
)) {
4633 gtk_text_iter_backward_char(&cur
);
4634 wc
= gtk_text_iter_get_char(&cur
);
4635 if (!g_unichar_isspace(wc
))
4639 if (!gtk_text_iter_equal(&prev
, &next
)) {
4640 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4642 gtk_text_iter_forward_char(&break_pos
);
4646 gtk_text_buffer_insert(buffer
, &break_pos
,
4650 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4652 /* move iter to current line start */
4653 gtk_text_iter_set_line_offset(&iter
, 0);
4660 /* move iter to next line start */
4666 if (!prev_autowrap
&& num_blocks
> 0) {
4668 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4669 G_CALLBACK(text_inserted
),
4673 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4674 gtk_text_iter_forward_char(&end_of_line
);
4676 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4678 nouri_start
= gtk_text_iter_get_offset(&iter
);
4679 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4681 walk_pos
= gtk_text_iter_get_offset(&iter
);
4682 /* FIXME: this looks phony. scanning for anything in the parse table */
4683 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4686 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4688 if (scanpos
== NULL
|| tmp
< scanpos
) {
4697 /* check if URI can be parsed */
4698 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4699 (const gchar
**)&ep
, FALSE
)
4700 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4704 strlen(parser
[last_index
].needle
);
4707 uri_start
= walk_pos
+ (bp
- o_walk
);
4708 uri_stop
= walk_pos
+ (ep
- o_walk
);
4712 gtk_text_iter_forward_line(&iter
);
4715 if (startq_offset
!= -1) {
4716 GtkTextIter startquote
, endquote
;
4717 gtk_text_buffer_get_iter_at_offset(
4718 buffer
, &startquote
, startq_offset
);
4721 switch (quotelevel
) {
4723 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4724 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4725 gtk_text_buffer_apply_tag_by_name(
4726 buffer
, "quote0", &startquote
, &endquote
);
4727 gtk_text_buffer_remove_tag_by_name(
4728 buffer
, "quote1", &startquote
, &endquote
);
4729 gtk_text_buffer_remove_tag_by_name(
4730 buffer
, "quote2", &startquote
, &endquote
);
4735 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4736 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4737 gtk_text_buffer_apply_tag_by_name(
4738 buffer
, "quote1", &startquote
, &endquote
);
4739 gtk_text_buffer_remove_tag_by_name(
4740 buffer
, "quote0", &startquote
, &endquote
);
4741 gtk_text_buffer_remove_tag_by_name(
4742 buffer
, "quote2", &startquote
, &endquote
);
4747 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4748 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4749 gtk_text_buffer_apply_tag_by_name(
4750 buffer
, "quote2", &startquote
, &endquote
);
4751 gtk_text_buffer_remove_tag_by_name(
4752 buffer
, "quote0", &startquote
, &endquote
);
4753 gtk_text_buffer_remove_tag_by_name(
4754 buffer
, "quote1", &startquote
, &endquote
);
4760 } else if (noq_offset
!= -1) {
4761 GtkTextIter startnoquote
, endnoquote
;
4762 gtk_text_buffer_get_iter_at_offset(
4763 buffer
, &startnoquote
, noq_offset
);
4766 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4767 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4768 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4769 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4770 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4771 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4772 gtk_text_buffer_remove_tag_by_name(
4773 buffer
, "quote0", &startnoquote
, &endnoquote
);
4774 gtk_text_buffer_remove_tag_by_name(
4775 buffer
, "quote1", &startnoquote
, &endnoquote
);
4776 gtk_text_buffer_remove_tag_by_name(
4777 buffer
, "quote2", &startnoquote
, &endnoquote
);
4783 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4784 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4785 gtk_text_buffer_get_iter_at_offset(
4786 buffer
, &nouri_start_iter
, nouri_start
);
4787 gtk_text_buffer_get_iter_at_offset(
4788 buffer
, &nouri_end_iter
, nouri_stop
);
4789 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4790 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4791 gtk_text_buffer_remove_tag_by_name(
4792 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4793 modified_before_remove
= modified
;
4798 if (uri_start
>= 0 && uri_stop
> 0) {
4799 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4800 gtk_text_buffer_get_iter_at_offset(
4801 buffer
, &uri_start_iter
, uri_start
);
4802 gtk_text_buffer_get_iter_at_offset(
4803 buffer
, &uri_end_iter
, uri_stop
);
4804 back
= uri_end_iter
;
4805 gtk_text_iter_backward_char(&back
);
4806 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4807 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4808 gtk_text_buffer_apply_tag_by_name(
4809 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4811 if (removed
&& !modified_before_remove
) {
4817 /* debug_print("not modified, out after %d lines\n", lines); */
4821 /* debug_print("modified, out after %d lines\n", lines); */
4823 g_free(itemized_chars
);
4826 undo_wrapping(compose
->undostruct
, FALSE
);
4827 compose
->autowrap
= prev_autowrap
;
4832 void compose_action_cb(void *data
)
4834 Compose
*compose
= (Compose
*)data
;
4835 compose_wrap_all(compose
);
4838 static void compose_wrap_all(Compose
*compose
)
4840 compose_wrap_all_full(compose
, FALSE
);
4843 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4845 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4846 GtkTextBuffer
*buffer
;
4848 gboolean modified
= TRUE
;
4850 buffer
= gtk_text_view_get_buffer(text
);
4852 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4854 undo_wrapping(compose
->undostruct
, TRUE
);
4856 while (!gtk_text_iter_is_end(&iter
) && modified
)
4857 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4859 undo_wrapping(compose
->undostruct
, FALSE
);
4863 static void compose_set_title(Compose
*compose
)
4869 edited
= compose
->modified
? _(" [Edited]") : "";
4871 subject
= gtk_editable_get_chars(
4872 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4874 #ifndef GENERIC_UMPC
4875 if (subject
&& strlen(subject
))
4876 str
= g_strdup_printf(_("%s - Compose message%s"),
4879 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4881 str
= g_strdup(_("Compose message"));
4884 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4890 * compose_current_mail_account:
4892 * Find a current mail account (the currently selected account, or the
4893 * default account, if a news account is currently selected). If a
4894 * mail account cannot be found, display an error message.
4896 * Return value: Mail account, or NULL if not found.
4898 static PrefsAccount
*
4899 compose_current_mail_account(void)
4903 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4906 ac
= account_get_default();
4907 if (!ac
|| ac
->protocol
== A_NNTP
) {
4908 alertpanel_error(_("Account for sending mail is not specified.\n"
4909 "Please select a mail account before sending."));
4916 #define QUOTE_IF_REQUIRED(out, str) \
4918 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4922 len = strlen(str) + 3; \
4923 if ((__tmp = alloca(len)) == NULL) { \
4924 g_warning("can't allocate memory"); \
4925 g_string_free(header, TRUE); \
4928 g_snprintf(__tmp, len, "\"%s\"", str); \
4933 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4934 g_warning("can't allocate memory"); \
4935 g_string_free(header, TRUE); \
4938 strcpy(__tmp, str); \
4944 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4946 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4950 len = strlen(str) + 3; \
4951 if ((__tmp = alloca(len)) == NULL) { \
4952 g_warning("can't allocate memory"); \
4955 g_snprintf(__tmp, len, "\"%s\"", str); \
4960 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4961 g_warning("can't allocate memory"); \
4964 strcpy(__tmp, str); \
4970 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4973 gchar
*from
= NULL
, *header
= NULL
;
4974 ComposeHeaderEntry
*header_entry
;
4977 cm_return_if_fail(account
!= NULL
);
4979 compose
->account
= account
;
4980 if (account
->name
&& *account
->name
) {
4982 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4983 qbuf
= escape_internal_quotes(buf
, '"');
4984 from
= g_strdup_printf("%s <%s>",
4985 qbuf
, account
->address
);
4988 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4990 from
= g_strdup_printf("<%s>",
4992 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4997 compose_set_title(compose
);
4999 compose_activate_privacy_system(compose
, account
, FALSE
);
5001 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
5002 compose
->mode
!= COMPOSE_REDIRECT
)
5003 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
5005 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
5006 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
5007 compose
->mode
!= COMPOSE_REDIRECT
)
5008 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
5010 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
5012 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
5013 undo_block(compose
->undostruct
);
5014 compose_insert_sig(compose
, TRUE
);
5015 undo_unblock(compose
->undostruct
);
5018 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5019 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5020 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5021 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5023 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5024 if (account
->protocol
== A_NNTP
) {
5025 if (!strcmp(header
, _("To:")))
5026 combobox_select_by_text(
5027 GTK_COMBO_BOX(header_entry
->combo
),
5030 if (!strcmp(header
, _("Newsgroups:")))
5031 combobox_select_by_text(
5032 GTK_COMBO_BOX(header_entry
->combo
),
5040 /* use account's dict info if set */
5041 if (compose
->gtkaspell
) {
5042 if (account
->enable_default_dictionary
)
5043 gtkaspell_change_dict(compose
->gtkaspell
,
5044 account
->default_dictionary
, FALSE
);
5045 if (account
->enable_default_alt_dictionary
)
5046 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5047 account
->default_alt_dictionary
);
5048 if (account
->enable_default_dictionary
5049 || account
->enable_default_alt_dictionary
)
5050 compose_spell_menu_changed(compose
);
5055 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5056 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5057 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5058 gboolean recipient_found
= FALSE
;
5062 /* free to and newsgroup list */
5063 slist_free_strings_full(compose
->to_list
);
5064 compose
->to_list
= NULL
;
5066 slist_free_strings_full(compose
->newsgroup_list
);
5067 compose
->newsgroup_list
= NULL
;
5069 /* search header entries for to and newsgroup entries */
5070 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5073 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5074 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5077 if (entry
[0] != '\0') {
5078 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5079 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5080 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5081 recipient_found
= TRUE
;
5084 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5085 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5086 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5087 recipient_found
= TRUE
;
5094 return recipient_found
;
5097 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5099 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5100 gboolean found_other
= FALSE
;
5102 /* search header entries for to and newsgroup entries */
5103 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5106 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5107 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5110 if (strcmp(entry
, compose
->account
->auto_cc
)
5111 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5122 if (compose
->batch
) {
5123 gtk_widget_show_all(compose
->window
);
5125 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5126 prefs_common_translated_header_name("Cc"));
5127 aval
= alertpanel(_("Send"),
5129 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5131 if (aval
!= G_ALERTALTERNATE
)
5135 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5136 gboolean found_other
= FALSE
;
5138 /* search header entries for to and newsgroup entries */
5139 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5142 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5143 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5146 if (strcmp(entry
, compose
->account
->auto_bcc
)
5147 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5159 if (compose
->batch
) {
5160 gtk_widget_show_all(compose
->window
);
5162 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5163 prefs_common_translated_header_name("Bcc"));
5164 aval
= alertpanel(_("Send"),
5166 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5168 if (aval
!= G_ALERTALTERNATE
)
5175 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5179 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5180 if (compose
->batch
) {
5181 gtk_widget_show_all(compose
->window
);
5183 alertpanel_error(_("Recipient is not specified."));
5187 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5191 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5192 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5193 if (*str
== '\0' && check_everything
== TRUE
&&
5194 compose
->mode
!= COMPOSE_REDIRECT
) {
5198 message
= g_strdup_printf(_("Subject is empty. %s"),
5199 compose
->sending
?_("Send it anyway?"):
5200 _("Queue it anyway?"));
5202 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5203 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5204 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5206 if (aval
& G_ALERTDISABLE
) {
5207 aval
&= ~G_ALERTDISABLE
;
5208 prefs_common
.warn_empty_subj
= FALSE
;
5210 if (aval
!= G_ALERTALTERNATE
)
5215 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5216 && check_everything
== TRUE
) {
5220 /* count To and Cc recipients */
5221 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5225 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5226 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5229 if ((entry
[0] != '\0') &&
5230 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5231 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5237 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5241 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5242 compose
->sending
?_("Send it anyway?"):
5243 _("Queue it anyway?"));
5245 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5246 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5247 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5249 if (aval
& G_ALERTDISABLE
) {
5250 aval
&= ~G_ALERTDISABLE
;
5251 prefs_common
.warn_sending_many_recipients_num
= 0;
5253 if (aval
!= G_ALERTALTERNATE
)
5258 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5264 static void _display_queue_error(ComposeQueueResult val
)
5267 case COMPOSE_QUEUE_SUCCESS
:
5269 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5270 alertpanel_error(_("Could not queue message."));
5272 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5273 alertpanel_error(_("Could not queue message:\n\n%s."),
5276 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5277 alertpanel_error(_("Could not queue message for sending:\n\n"
5278 "Signature failed: %s"),
5279 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5281 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5282 alertpanel_error(_("Could not queue message for sending:\n\n"
5283 "Encryption failed: %s"),
5284 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5286 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5287 alertpanel_error(_("Could not queue message for sending:\n\n"
5288 "Charset conversion failed."));
5290 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5291 alertpanel_error(_("Could not queue message for sending:\n\n"
5292 "Couldn't get recipient encryption key."));
5294 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5295 debug_print("signing cancelled\n");
5298 /* unhandled error */
5299 debug_print("oops, unhandled compose_queue() return value %d\n",
5305 gint
compose_send(Compose
*compose
)
5308 FolderItem
*folder
= NULL
;
5309 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5310 gchar
*msgpath
= NULL
;
5311 gboolean discard_window
= FALSE
;
5312 gchar
*errstr
= NULL
;
5313 gchar
*tmsgid
= NULL
;
5314 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5315 gboolean queued_removed
= FALSE
;
5317 if (prefs_common
.send_dialog_invisible
5318 || compose
->batch
== TRUE
)
5319 discard_window
= TRUE
;
5321 compose_allow_user_actions (compose
, FALSE
);
5322 compose
->sending
= TRUE
;
5324 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5325 if (compose
->batch
) {
5326 gtk_widget_show_all(compose
->window
);
5332 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5334 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5335 if (compose
->batch
) {
5336 gtk_widget_show_all(compose
->window
);
5339 _display_queue_error(val
);
5344 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5345 if (discard_window
) {
5346 compose
->sending
= FALSE
;
5347 compose_close(compose
);
5348 /* No more compose access in the normal codepath
5349 * after this point! */
5354 alertpanel_error(_("The message was queued but could not be "
5355 "sent.\nUse \"Send queued messages\" from "
5356 "the main window to retry."));
5357 if (!discard_window
) {
5364 if (msgpath
== NULL
) {
5365 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5366 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5369 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5370 claws_unlink(msgpath
);
5373 if (!discard_window
) {
5375 if (!queued_removed
)
5376 folder_item_remove_msg(folder
, msgnum
);
5377 folder_item_scan(folder
);
5379 /* make sure we delete that */
5380 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5382 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5383 folder_item_remove_msg(folder
, tmp
->msgnum
);
5384 procmsg_msginfo_free(&tmp
);
5391 if (!queued_removed
)
5392 folder_item_remove_msg(folder
, msgnum
);
5393 folder_item_scan(folder
);
5395 /* make sure we delete that */
5396 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5398 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5399 folder_item_remove_msg(folder
, tmp
->msgnum
);
5400 procmsg_msginfo_free(&tmp
);
5403 if (!discard_window
) {
5404 compose
->sending
= FALSE
;
5405 compose_allow_user_actions (compose
, TRUE
);
5406 compose_close(compose
);
5410 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5411 "the main window to retry."), errstr
);
5414 alertpanel_error_log(_("The message was queued but could not be "
5415 "sent.\nUse \"Send queued messages\" from "
5416 "the main window to retry."));
5418 if (!discard_window
) {
5427 toolbar_main_set_sensitive(mainwin
);
5428 main_window_set_menu_sensitive(mainwin
);
5434 compose_allow_user_actions (compose
, TRUE
);
5435 compose
->sending
= FALSE
;
5436 compose
->modified
= TRUE
;
5437 toolbar_main_set_sensitive(mainwin
);
5438 main_window_set_menu_sensitive(mainwin
);
5443 static gboolean
compose_use_attach(Compose
*compose
)
5445 GtkTreeModel
*model
= gtk_tree_view_get_model
5446 (GTK_TREE_VIEW(compose
->attach_clist
));
5447 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5450 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5453 gchar buf
[BUFFSIZE
];
5455 gboolean first_to_address
;
5456 gboolean first_cc_address
;
5458 ComposeHeaderEntry
*headerentry
;
5459 const gchar
*headerentryname
;
5460 const gchar
*cc_hdr
;
5461 const gchar
*to_hdr
;
5462 gboolean err
= FALSE
;
5464 debug_print("Writing redirect header\n");
5466 cc_hdr
= prefs_common_translated_header_name("Cc:");
5467 to_hdr
= prefs_common_translated_header_name("To:");
5469 first_to_address
= TRUE
;
5470 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5471 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5472 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5474 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5475 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5476 Xstrdup_a(str
, entstr
, return -1);
5478 if (str
[0] != '\0') {
5479 compose_convert_header
5480 (compose
, buf
, sizeof(buf
), str
,
5481 strlen("Resent-To") + 2, TRUE
);
5483 if (first_to_address
) {
5484 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5485 first_to_address
= FALSE
;
5487 err
|= (fprintf(fp
, ",") < 0);
5489 err
|= (fprintf(fp
, "%s", buf
) < 0);
5493 if (!first_to_address
) {
5494 err
|= (fprintf(fp
, "\n") < 0);
5497 first_cc_address
= TRUE
;
5498 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5499 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5500 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5502 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5503 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5504 Xstrdup_a(str
, strg
, return -1);
5506 if (str
[0] != '\0') {
5507 compose_convert_header
5508 (compose
, buf
, sizeof(buf
), str
,
5509 strlen("Resent-Cc") + 2, TRUE
);
5511 if (first_cc_address
) {
5512 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5513 first_cc_address
= FALSE
;
5515 err
|= (fprintf(fp
, ",") < 0);
5517 err
|= (fprintf(fp
, "%s", buf
) < 0);
5521 if (!first_cc_address
) {
5522 err
|= (fprintf(fp
, "\n") < 0);
5525 return (err
? -1:0);
5528 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5530 gchar date
[RFC822_DATE_BUFFSIZE
];
5531 gchar buf
[BUFFSIZE
];
5533 const gchar
*entstr
;
5534 /* struct utsname utsbuf; */
5535 gboolean err
= FALSE
;
5537 cm_return_val_if_fail(fp
!= NULL
, -1);
5538 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5539 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5542 if (prefs_common
.hide_timezone
)
5543 get_rfc822_date_hide_tz(date
, sizeof(date
));
5545 get_rfc822_date(date
, sizeof(date
));
5546 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5549 if (compose
->account
->name
&& *compose
->account
->name
) {
5550 compose_convert_header
5551 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5552 strlen("From: "), TRUE
);
5553 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5554 buf
, compose
->account
->address
) < 0);
5556 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5559 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5560 if (*entstr
!= '\0') {
5561 Xstrdup_a(str
, entstr
, return -1);
5564 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5565 strlen("Subject: "), FALSE
);
5566 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5570 /* Resent-Message-ID */
5571 if (compose
->account
->gen_msgid
) {
5572 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5573 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5575 g_free(compose
->msgid
);
5576 compose
->msgid
= addr
;
5578 compose
->msgid
= NULL
;
5581 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5584 /* separator between header and body */
5585 err
|= (claws_fputs("\n", fp
) == EOF
);
5587 return (err
? -1:0);
5590 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5595 gchar rewrite_buf
[BUFFSIZE
];
5597 gboolean skip
= FALSE
;
5598 gboolean err
= FALSE
;
5599 gchar
*not_included
[]={
5600 "Return-Path:", "Delivered-To:", "Received:",
5601 "Subject:", "X-UIDL:", "AF:",
5602 "NF:", "PS:", "SRH:",
5603 "SFN:", "DSR:", "MID:",
5604 "CFG:", "PT:", "S:",
5605 "RQ:", "SSV:", "NSV:",
5606 "SSH:", "R:", "MAID:",
5607 "NAID:", "RMID:", "FMID:",
5608 "SCF:", "RRCPT:", "NG:",
5609 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5610 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5611 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5612 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5613 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5618 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5619 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5623 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5625 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5626 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5627 strlen(not_included
[i
])) == 0) {
5637 if (claws_fputs(buf
, fdest
) == -1) {
5643 if (!prefs_common
.redirect_keep_from
) {
5644 if (g_ascii_strncasecmp(buf
, "From:",
5645 strlen("From:")) == 0) {
5646 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5647 if (compose
->account
->name
5648 && *compose
->account
->name
) {
5649 gchar buffer
[BUFFSIZE
];
5651 compose_convert_header
5652 (compose
, buffer
, sizeof(buffer
),
5653 compose
->account
->name
,
5656 err
|= (fprintf(fdest
, "%s <%s>",
5658 compose
->account
->address
) < 0);
5660 err
|= (fprintf(fdest
, "%s",
5661 compose
->account
->address
) < 0);
5662 err
|= (claws_fputs(")", fdest
) == EOF
);
5668 if (claws_fputs("\n", fdest
) == -1)
5675 if (compose_redirect_write_headers(compose
, fdest
))
5678 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5679 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5693 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5695 GtkTextBuffer
*buffer
;
5696 GtkTextIter start
, end
, tmp
;
5697 gchar
*chars
, *tmp_enc_file
, *content
;
5699 const gchar
*out_codeset
;
5700 EncodingType encoding
= ENC_UNKNOWN
;
5701 MimeInfo
*mimemsg
, *mimetext
;
5703 const gchar
*src_codeset
= CS_INTERNAL
;
5704 gchar
*from_addr
= NULL
;
5705 gchar
*from_name
= NULL
;
5708 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5709 attach_parts
= TRUE
;
5711 /* We're sending the message, generate a Message-ID
5713 if (compose
->msgid
== NULL
&&
5714 compose
->account
->gen_msgid
) {
5715 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5719 /* create message MimeInfo */
5720 mimemsg
= procmime_mimeinfo_new();
5721 mimemsg
->type
= MIMETYPE_MESSAGE
;
5722 mimemsg
->subtype
= g_strdup("rfc822");
5723 mimemsg
->content
= MIMECONTENT_MEM
;
5724 mimemsg
->tmp
= TRUE
; /* must free content later */
5725 mimemsg
->data
.mem
= compose_get_header(compose
);
5727 /* Create text part MimeInfo */
5728 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5729 gtk_text_buffer_get_end_iter(buffer
, &end
);
5732 /* We make sure that there is a newline at the end. */
5733 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5734 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5735 if (*chars
!= '\n') {
5736 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5741 /* get all composed text */
5742 gtk_text_buffer_get_start_iter(buffer
, &start
);
5743 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5745 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5747 if (!out_codeset
&& is_ascii_str(chars
)) {
5748 out_codeset
= CS_US_ASCII
;
5749 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5750 is_ascii_str(chars
)) {
5751 out_codeset
= CS_US_ASCII
;
5752 encoding
= ENC_7BIT
;
5756 gchar
*test_conv_global_out
= NULL
;
5757 gchar
*test_conv_reply
= NULL
;
5759 /* automatic mode. be automatic. */
5760 codeconv_set_strict(TRUE
);
5762 out_codeset
= conv_get_outgoing_charset_str();
5764 debug_print("trying to convert to %s\n", out_codeset
);
5765 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5768 if (!test_conv_global_out
&& compose
->orig_charset
5769 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5770 out_codeset
= compose
->orig_charset
;
5771 debug_print("failure; trying to convert to %s\n", out_codeset
);
5772 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5775 if (!test_conv_global_out
&& !test_conv_reply
) {
5777 out_codeset
= CS_INTERNAL
;
5778 debug_print("failure; finally using %s\n", out_codeset
);
5780 g_free(test_conv_global_out
);
5781 g_free(test_conv_reply
);
5782 codeconv_set_strict(FALSE
);
5785 if (encoding
== ENC_UNKNOWN
) {
5786 if (prefs_common
.encoding_method
== CTE_BASE64
)
5787 encoding
= ENC_BASE64
;
5788 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5789 encoding
= ENC_QUOTED_PRINTABLE
;
5790 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5791 encoding
= ENC_8BIT
;
5793 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5796 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5797 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5799 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5800 codeconv_set_strict(TRUE
);
5801 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5802 codeconv_set_strict(FALSE
);
5807 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5808 "to the specified %s charset.\n"
5809 "Send it as %s?"), out_codeset
, src_codeset
);
5810 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
,
5811 _("_Send"), NULL
, ALERTFOCUS_SECOND
, FALSE
,
5815 if (aval
!= G_ALERTALTERNATE
) {
5817 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5820 out_codeset
= src_codeset
;
5826 out_codeset
= src_codeset
;
5831 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5832 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5833 strstr(buf
, "\nFrom ") != NULL
) {
5834 encoding
= ENC_QUOTED_PRINTABLE
;
5838 mimetext
= procmime_mimeinfo_new();
5839 mimetext
->content
= MIMECONTENT_MEM
;
5840 mimetext
->tmp
= TRUE
; /* must free content later */
5841 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5842 * and free the data, which we need later. */
5843 mimetext
->data
.mem
= g_strdup(buf
);
5844 mimetext
->type
= MIMETYPE_TEXT
;
5845 mimetext
->subtype
= g_strdup("plain");
5846 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5847 g_strdup(out_codeset
));
5849 /* protect trailing spaces when signing message */
5850 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5851 privacy_system_can_sign(compose
->privacy_system
)) {
5852 encoding
= ENC_QUOTED_PRINTABLE
;
5856 debug_print("main text: %Id bytes encoded as %s in %d\n",
5858 debug_print("main text: %zd bytes encoded as %s in %d\n",
5860 strlen(buf
), out_codeset
, encoding
);
5862 /* check for line length limit */
5863 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5864 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5865 check_line_length(buf
, 1000, &line
) < 0) {
5868 msg
= g_strdup_printf
5869 (_("Line %d exceeds the line length limit (998 bytes).\n"
5870 "The contents of the message might be broken on the way to the delivery.\n"
5872 "Send it anyway?"), line
+ 1);
5873 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
,
5876 if (aval
!= G_ALERTALTERNATE
) {
5878 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5882 if (encoding
!= ENC_UNKNOWN
)
5883 procmime_encode_content(mimetext
, encoding
);
5885 /* append attachment parts */
5886 if (compose_use_attach(compose
) && attach_parts
) {
5887 MimeInfo
*mimempart
;
5888 gchar
*boundary
= NULL
;
5889 mimempart
= procmime_mimeinfo_new();
5890 mimempart
->content
= MIMECONTENT_EMPTY
;
5891 mimempart
->type
= MIMETYPE_MULTIPART
;
5892 mimempart
->subtype
= g_strdup("mixed");
5896 boundary
= generate_mime_boundary(NULL
);
5897 } while (strstr(buf
, boundary
) != NULL
);
5899 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5902 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5904 g_node_append(mimempart
->node
, mimetext
->node
);
5905 g_node_append(mimemsg
->node
, mimempart
->node
);
5907 if (compose_add_attachments(compose
, mimempart
) < 0)
5908 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5910 g_node_append(mimemsg
->node
, mimetext
->node
);
5914 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5915 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5916 /* extract name and address */
5917 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5918 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5919 *(strrchr(from_addr
, '>')) = '\0';
5920 from_name
= g_strdup(spec
);
5921 *(strrchr(from_name
, '<')) = '\0';
5928 /* sign message if sending */
5929 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5930 privacy_system_can_sign(compose
->privacy_system
))
5931 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5932 compose
->account
, from_addr
)) {
5935 if (!privacy_peek_error())
5936 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5938 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5943 if (compose
->use_encryption
) {
5944 if (compose
->encdata
!= NULL
&&
5945 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5947 /* First, write an unencrypted copy and save it to outbox, if
5948 * user wants that. */
5949 if (compose
->account
->save_encrypted_as_clear_text
) {
5950 debug_print("saving sent message unencrypted...\n");
5951 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5953 claws_fclose(tmpfp
);
5955 /* fp now points to a file with headers written,
5956 * let's make a copy. */
5958 content
= file_read_stream_to_str(fp
);
5960 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5963 /* Now write the unencrypted body. */
5964 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5965 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5966 claws_fclose(tmpfp
);
5968 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5970 outbox
= folder_get_default_outbox();
5972 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5973 claws_unlink(tmp_enc_file
);
5975 g_warning("Can't open file '%s'", tmp_enc_file
);
5978 g_warning("couldn't get tempfile");
5981 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5982 debug_print("Couldn't encrypt mime structure: %s.\n",
5983 privacy_get_error());
5984 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5989 procmime_write_mimeinfo(mimemsg
, fp
);
5991 procmime_mimeinfo_free_all(&mimemsg
);
5996 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5998 GtkTextBuffer
*buffer
;
5999 GtkTextIter start
, end
;
6004 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
6005 FILE_OP_ERROR(file
, "claws_fopen");
6009 /* chmod for security */
6010 if (change_file_mode_rw(fp
, file
) < 0) {
6011 FILE_OP_ERROR(file
, "chmod");
6012 g_warning("can't change file mode");
6015 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
6016 gtk_text_buffer_get_start_iter(buffer
, &start
);
6017 gtk_text_buffer_get_end_iter(buffer
, &end
);
6018 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6020 chars
= conv_codeset_strdup
6021 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6030 len
= strlen(chars
);
6031 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6032 FILE_OP_ERROR(file
, "claws_fwrite");
6041 if (claws_safe_fclose(fp
) == EOF
) {
6042 FILE_OP_ERROR(file
, "claws_fclose");
6049 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6052 MsgInfo
*msginfo
= compose
->targetinfo
;
6054 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6055 if (!msginfo
) return -1;
6057 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6060 item
= msginfo
->folder
;
6061 cm_return_val_if_fail(item
!= NULL
, -1);
6063 if (procmsg_msg_exist(msginfo
) &&
6064 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6065 folder_has_parent_of_type(item
, F_DRAFT
)
6066 || msginfo
== compose
->autosaved_draft
)) {
6067 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6068 g_warning("can't remove the old message");
6071 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6078 static void compose_remove_draft(Compose
*compose
)
6081 MsgInfo
*msginfo
= compose
->targetinfo
;
6082 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6084 if (procmsg_msg_exist(msginfo
)) {
6085 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6090 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6091 gboolean remove_reedit_target
)
6093 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6096 static gboolean
compose_warn_encryption(Compose
*compose
)
6098 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6099 AlertValue val
= G_ALERTALTERNATE
;
6101 if (warning
== NULL
)
6104 val
= alertpanel_full(_("Encryption warning"), warning
,
6105 GTK_STOCK_CANCEL
, _("C_ontinue"), NULL
, ALERTFOCUS_SECOND
,
6106 TRUE
, NULL
, ALERT_WARNING
);
6107 if (val
& G_ALERTDISABLE
) {
6108 val
&= ~G_ALERTDISABLE
;
6109 if (val
== G_ALERTALTERNATE
)
6110 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6114 if (val
== G_ALERTALTERNATE
) {
6121 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6122 gchar
**msgpath
, gboolean perform_checks
,
6123 gboolean remove_reedit_target
)
6130 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6131 gboolean err
= FALSE
;
6133 debug_print("queueing message...\n");
6134 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6136 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6137 if (compose
->batch
) {
6138 gtk_widget_show_all(compose
->window
);
6140 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6143 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6144 g_warning("can't get recipient list.");
6145 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6148 if (compose
->to_list
) {
6149 if (compose
->account
->protocol
!= A_NNTP
)
6150 mailac
= compose
->account
;
6151 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
6152 mailac
= cur_account
;
6153 else if (!(mailac
= compose_current_mail_account())) {
6154 alertpanel_error(_("No account for sending mails available!"));
6155 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6159 if (compose
->newsgroup_list
) {
6160 if (compose
->account
->protocol
== A_NNTP
)
6161 newsac
= compose
->account
;
6163 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6164 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6168 /* write queue header */
6169 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6170 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6171 debug_print("queuing to %s\n", tmp
);
6172 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6173 FILE_OP_ERROR(tmp
, "claws_fopen");
6175 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6178 if (change_file_mode_rw(fp
, tmp
) < 0) {
6179 FILE_OP_ERROR(tmp
, "chmod");
6180 g_warning("can't change file mode");
6183 /* queueing variables */
6184 err
|= (fprintf(fp
, "AF:\n") < 0);
6185 err
|= (fprintf(fp
, "NF:0\n") < 0);
6186 err
|= (fprintf(fp
, "PS:10\n") < 0);
6187 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6188 err
|= (fprintf(fp
, "SFN:\n") < 0);
6189 err
|= (fprintf(fp
, "DSR:\n") < 0);
6191 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6193 err
|= (fprintf(fp
, "MID:\n") < 0);
6194 err
|= (fprintf(fp
, "CFG:\n") < 0);
6195 err
|= (fprintf(fp
, "PT:0\n") < 0);
6196 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6197 err
|= (fprintf(fp
, "RQ:\n") < 0);
6199 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6201 err
|= (fprintf(fp
, "SSV:\n") < 0);
6203 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6205 err
|= (fprintf(fp
, "NSV:\n") < 0);
6206 err
|= (fprintf(fp
, "SSH:\n") < 0);
6207 /* write recipient list */
6208 if (compose
->to_list
) {
6209 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6210 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6212 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6213 err
|= (fprintf(fp
, "\n") < 0);
6215 /* write newsgroup list */
6216 if (compose
->newsgroup_list
) {
6217 err
|= (fprintf(fp
, "NG:") < 0);
6218 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6219 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6220 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6221 err
|= (fprintf(fp
, "\n") < 0);
6225 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6227 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6230 if (compose
->privacy_system
!= NULL
) {
6231 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6232 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6233 if (compose
->use_encryption
) {
6234 if (!compose_warn_encryption(compose
)) {
6238 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6240 if (mailac
&& mailac
->encrypt_to_self
) {
6241 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6242 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6243 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6244 g_slist_free(tmp_list
);
6246 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6248 if (compose
->encdata
!= NULL
) {
6249 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6250 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6251 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6252 compose
->encdata
) < 0);
6253 } /* else we finally dont want to encrypt */
6255 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6256 /* and if encdata was null, it means there's been a problem in
6259 g_warning("failed to write queue message");
6263 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6268 /* Save copy folder */
6269 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6270 gchar
*savefolderid
;
6272 savefolderid
= compose_get_save_to(compose
);
6273 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6274 g_free(savefolderid
);
6276 /* Save copy folder */
6277 if (compose
->return_receipt
) {
6278 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6280 /* Message-ID of message replying to */
6281 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6282 gchar
*folderid
= NULL
;
6284 if (compose
->replyinfo
->folder
)
6285 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6286 if (folderid
== NULL
)
6287 folderid
= g_strdup("NULL");
6289 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6292 /* Message-ID of message forwarding to */
6293 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6294 gchar
*folderid
= NULL
;
6296 if (compose
->fwdinfo
->folder
)
6297 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6298 if (folderid
== NULL
)
6299 folderid
= g_strdup("NULL");
6301 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6305 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6306 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6308 /* end of headers */
6309 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6311 if (compose
->redirect_filename
!= NULL
) {
6312 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6316 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6320 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6328 g_warning("failed to write queue message");
6332 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6334 if (claws_safe_fclose(fp
) == EOF
) {
6335 FILE_OP_ERROR(tmp
, "claws_fclose");
6338 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6341 if (item
&& *item
) {
6344 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6347 g_warning("can't find queue folder");
6350 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6352 folder_item_scan(queue
);
6353 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6354 g_warning("can't queue the message");
6357 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6360 if (msgpath
== NULL
) {
6366 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6367 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6369 procmsg_msginfo_change_flags(mi
,
6370 compose
->targetinfo
->flags
.perm_flags
,
6371 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6374 g_slist_free(mi
->tags
);
6375 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6376 procmsg_msginfo_free(&mi
);
6380 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6381 compose_remove_reedit_target(compose
, FALSE
);
6384 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6389 return COMPOSE_QUEUE_SUCCESS
;
6392 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6395 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6400 GError
*error
= NULL
;
6405 gchar
*type
, *subtype
;
6406 GtkTreeModel
*model
;
6409 model
= gtk_tree_view_get_model(tree_view
);
6411 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6414 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6416 if (!is_file_exist(ainfo
->file
)) {
6417 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6418 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6419 _("Cancel sending"), _("Ignore attachment"), NULL
,
6420 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6422 if (val
== G_ALERTDEFAULT
) {
6428 f
= g_file_new_for_path(ainfo
->file
);
6429 fi
= g_file_query_info(f
, "standard::size",
6430 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6431 if (error
!= NULL
) {
6432 g_warning(error
->message
);
6433 g_error_free(error
);
6437 size
= g_file_info_get_size(fi
);
6441 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6443 size
= statbuf
.st_size
;
6446 mimepart
= procmime_mimeinfo_new();
6447 mimepart
->content
= MIMECONTENT_FILE
;
6448 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6449 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6450 mimepart
->offset
= 0;
6451 mimepart
->length
= size
;
6453 type
= g_strdup(ainfo
->content_type
);
6455 if (!strchr(type
, '/')) {
6457 type
= g_strdup("application/octet-stream");
6460 subtype
= strchr(type
, '/') + 1;
6461 *(subtype
- 1) = '\0';
6462 mimepart
->type
= procmime_get_media_type(type
);
6463 mimepart
->subtype
= g_strdup(subtype
);
6466 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6467 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6468 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6469 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6470 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6471 /* Text parts with no name come from multipart/alternative
6472 * forwards. Make sure the recipient won't look at the
6473 * original HTML part by mistake. */
6474 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6475 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6479 g_hash_table_insert(mimepart
->typeparameters
,
6480 g_strdup("charset"), g_strdup(ainfo
->charset
));
6482 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6483 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6484 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6485 g_hash_table_insert(mimepart
->typeparameters
,
6486 g_strdup("name"), g_strdup(ainfo
->name
));
6487 g_hash_table_insert(mimepart
->dispositionparameters
,
6488 g_strdup("filename"), g_strdup(ainfo
->name
));
6489 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6492 if (mimepart
->type
== MIMETYPE_MESSAGE
6493 || mimepart
->type
== MIMETYPE_MULTIPART
)
6494 ainfo
->encoding
= ENC_BINARY
;
6495 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6496 if (ainfo
->encoding
== ENC_7BIT
)
6497 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6498 else if (ainfo
->encoding
== ENC_8BIT
)
6499 ainfo
->encoding
= ENC_BASE64
;
6502 procmime_encode_content(mimepart
, ainfo
->encoding
);
6504 g_node_append(parent
->node
, mimepart
->node
);
6505 } while (gtk_tree_model_iter_next(model
, &iter
));
6510 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6512 GSList
*list
= NULL
, *item
= NULL
;
6513 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6515 list
= address_list_append_with_comments(list
, str
);
6516 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6517 gchar
*spec
= item
->data
;
6518 gchar
*endofname
= strstr(spec
, " <");
6519 if (endofname
!= NULL
) {
6522 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6523 qqname
= escape_internal_quotes(qname
, '"');
6525 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6526 gchar
*addr
= g_strdup(endofname
);
6527 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6528 faddr
= g_strconcat(name
, addr
, NULL
);
6531 debug_print("new auto-quoted address: '%s'\n", faddr
);
6535 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6537 result
= g_strconcat(result
,
6539 (faddr
!= NULL
)? faddr
: spec
,
6542 if (faddr
!= NULL
) {
6547 slist_free_strings_full(list
);
6552 #define IS_IN_CUSTOM_HEADER(header) \
6553 (compose->account->add_customhdr && \
6554 custom_header_find(compose->account->customhdr_list, header) != NULL)
6556 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6558 /* return the untranslated header name, if header_name is a known
6559 header name, in either its translated or untranslated form, with
6560 or without trailing colon. otherwise, returns header_name. */
6561 gchar
*translated_header_name
;
6562 gchar
*translated_header_name_wcolon
;
6563 const gchar
*untranslated_header_name
;
6564 const gchar
*untranslated_header_name_wcolon
;
6567 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6569 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6570 untranslated_header_name
= HEADERS
[i
].header_name
;
6571 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6573 translated_header_name
= gettext(untranslated_header_name
);
6574 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6576 if (!strcmp(header_name
, untranslated_header_name
) ||
6577 !strcmp(header_name
, translated_header_name
)) {
6578 return untranslated_header_name
;
6580 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6581 !strcmp(header_name
, translated_header_name_wcolon
)) {
6582 return untranslated_header_name_wcolon
;
6586 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6590 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6592 const gchar
*fieldname
,
6593 const gchar
*seperator
)
6595 gchar
*str
, *fieldname_w_colon
;
6596 gboolean add_field
= FALSE
;
6598 ComposeHeaderEntry
*headerentry
;
6599 const gchar
*headerentryname
;
6600 const gchar
*trans_fieldname
;
6603 if (IS_IN_CUSTOM_HEADER(fieldname
))
6606 debug_print("Adding %s-fields\n", fieldname
);
6608 fieldstr
= g_string_sized_new(64);
6610 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6611 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6613 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6614 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6615 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6617 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6618 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6620 str
= compose_quote_list_of_addresses(ustr
);
6622 if (str
!= NULL
&& str
[0] != '\0') {
6624 g_string_append(fieldstr
, seperator
);
6625 g_string_append(fieldstr
, str
);
6634 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6635 compose_convert_header
6636 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6637 strlen(fieldname
) + 2, TRUE
);
6638 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6642 g_free(fieldname_w_colon
);
6643 g_string_free(fieldstr
, TRUE
);
6648 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6650 GString
*sh_header
= g_string_new(" ");
6652 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6654 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6655 ComposeHeaderEntry
*headerentry
;
6658 gchar
*headername_wcolon
;
6659 const gchar
*headername_trans
;
6661 gboolean standard_header
= FALSE
;
6663 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6665 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6667 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6672 if (!strstr(tmp
, ":")) {
6673 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6674 headername
= g_strdup(tmp
);
6676 headername_wcolon
= g_strdup(tmp
);
6677 headername
= g_strdup(strtok(tmp
, ":"));
6681 string
= std_headers
;
6682 while (*string
!= NULL
) {
6683 headername_trans
= prefs_common_translated_header_name(*string
);
6684 if (!strcmp(headername_trans
, headername_wcolon
))
6685 standard_header
= TRUE
;
6688 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6689 g_string_append_printf(sh_header
, "%s ", headername
);
6691 g_free(headername_wcolon
);
6693 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6694 return g_string_free(sh_header
, FALSE
);
6697 static gchar
*compose_get_header(Compose
*compose
)
6699 gchar date
[RFC822_DATE_BUFFSIZE
];
6700 gchar buf
[BUFFSIZE
];
6701 const gchar
*entry_str
;
6705 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6707 gchar
*from_name
= NULL
, *from_address
= NULL
;
6710 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6711 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6713 header
= g_string_sized_new(64);
6716 if (prefs_common
.hide_timezone
)
6717 get_rfc822_date_hide_tz(date
, sizeof(date
));
6719 get_rfc822_date(date
, sizeof(date
));
6720 g_string_append_printf(header
, "Date: %s\n", date
);
6724 if (compose
->account
->name
&& *compose
->account
->name
) {
6726 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6727 tmp
= g_strdup_printf("%s <%s>",
6728 buf
, compose
->account
->address
);
6730 tmp
= g_strdup_printf("%s",
6731 compose
->account
->address
);
6733 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6734 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6736 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6737 from_address
= g_strdup(compose
->account
->address
);
6739 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6740 /* extract name and address */
6741 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6742 from_address
= g_strdup(strrchr(spec
, '<')+1);
6743 *(strrchr(from_address
, '>')) = '\0';
6744 from_name
= g_strdup(spec
);
6745 *(strrchr(from_name
, '<')) = '\0';
6748 from_address
= g_strdup(spec
);
6755 if (from_name
&& *from_name
) {
6757 compose_convert_header
6758 (compose
, buf
, sizeof(buf
), from_name
,
6759 strlen("From: "), TRUE
);
6760 QUOTE_IF_REQUIRED(name
, buf
);
6761 qname
= escape_internal_quotes(name
, '"');
6763 g_string_append_printf(header
, "From: %s <%s>\n",
6764 qname
, from_address
);
6765 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6766 compose
->return_receipt
) {
6767 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6768 strlen("Disposition-Notification-To: "),
6770 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6775 g_string_append_printf(header
, "From: %s\n", from_address
);
6776 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6777 compose
->return_receipt
)
6778 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6782 g_free(from_address
);
6785 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6788 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6791 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6795 * If this account is a NNTP account remove Bcc header from
6796 * message body since it otherwise will be publicly shown
6798 if (compose
->account
->protocol
!= A_NNTP
)
6799 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6802 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6804 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6807 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6808 strlen("Subject: "), FALSE
);
6809 g_string_append_printf(header
, "Subject: %s\n", buf
);
6815 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6816 g_string_append_printf(header
, "Message-ID: <%s>\n",
6820 if (compose
->remove_references
== FALSE
) {
6822 if (compose
->inreplyto
&& compose
->to_list
)
6823 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6826 if (compose
->references
)
6827 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6831 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6834 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6837 if (compose
->account
->organization
&&
6838 strlen(compose
->account
->organization
) &&
6839 !IS_IN_CUSTOM_HEADER("Organization")) {
6840 compose_convert_header(compose
, buf
, sizeof(buf
),
6841 compose
->account
->organization
,
6842 strlen("Organization: "), FALSE
);
6843 g_string_append_printf(header
, "Organization: %s\n", buf
);
6846 /* Program version and system info */
6847 if (compose
->account
->gen_xmailer
&&
6848 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6849 !compose
->newsgroup_list
) {
6850 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6852 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6855 if (compose
->account
->gen_xmailer
&&
6856 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6857 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6859 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6863 /* custom headers */
6864 if (compose
->account
->add_customhdr
) {
6867 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6869 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6871 if (custom_header_is_allowed(chdr
->name
)
6872 && chdr
->value
!= NULL
6873 && *(chdr
->value
) != '\0') {
6874 compose_convert_header
6875 (compose
, buf
, sizeof(buf
),
6877 strlen(chdr
->name
) + 2, FALSE
);
6878 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6883 /* Automatic Faces and X-Faces */
6884 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6885 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6887 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6888 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6890 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6891 g_string_append_printf(header
, "Face: %s\n", buf
);
6893 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6894 g_string_append_printf(header
, "Face: %s\n", buf
);
6898 switch (compose
->priority
) {
6899 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6900 "X-Priority: 1 (Highest)\n");
6902 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6903 "X-Priority: 2 (High)\n");
6905 case PRIORITY_NORMAL
: break;
6906 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6907 "X-Priority: 4 (Low)\n");
6909 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6910 "X-Priority: 5 (Lowest)\n");
6912 default: debug_print("compose: priority unknown : %d\n",
6916 /* get special headers */
6917 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6918 ComposeHeaderEntry
*headerentry
;
6921 gchar
*headername_wcolon
;
6922 const gchar
*headername_trans
;
6925 gboolean standard_header
= FALSE
;
6927 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6929 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6931 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6936 if (!strstr(tmp
, ":")) {
6937 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6938 headername
= g_strdup(tmp
);
6940 headername_wcolon
= g_strdup(tmp
);
6941 headername
= g_strdup(strtok(tmp
, ":"));
6945 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6946 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6947 subst_char(headervalue
, '\r', ' ');
6948 subst_char(headervalue
, '\n', ' ');
6949 g_strstrip(headervalue
);
6950 if (*headervalue
!= '\0') {
6951 string
= std_headers
;
6952 while (*string
!= NULL
&& !standard_header
) {
6953 headername_trans
= prefs_common_translated_header_name(*string
);
6954 /* support mixed translated and untranslated headers */
6955 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6956 standard_header
= TRUE
;
6959 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6960 /* store untranslated header name */
6961 g_string_append_printf(header
, "%s %s\n",
6962 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6966 g_free(headername_wcolon
);
6970 g_string_free(header
, FALSE
);
6975 #undef IS_IN_CUSTOM_HEADER
6977 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6978 gint header_len
, gboolean addr_field
)
6980 gchar
*tmpstr
= NULL
;
6981 const gchar
*out_codeset
= NULL
;
6983 cm_return_if_fail(src
!= NULL
);
6984 cm_return_if_fail(dest
!= NULL
);
6986 if (len
< 1) return;
6988 tmpstr
= g_strdup(src
);
6990 subst_char(tmpstr
, '\n', ' ');
6991 subst_char(tmpstr
, '\r', ' ');
6994 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6995 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6996 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
7001 codeconv_set_strict(TRUE
);
7002 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7003 conv_get_charset_str(compose
->out_encoding
));
7004 codeconv_set_strict(FALSE
);
7006 if (!dest
|| *dest
== '\0') {
7007 gchar
*test_conv_global_out
= NULL
;
7008 gchar
*test_conv_reply
= NULL
;
7010 /* automatic mode. be automatic. */
7011 codeconv_set_strict(TRUE
);
7013 out_codeset
= conv_get_outgoing_charset_str();
7015 debug_print("trying to convert to %s\n", out_codeset
);
7016 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7019 if (!test_conv_global_out
&& compose
->orig_charset
7020 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7021 out_codeset
= compose
->orig_charset
;
7022 debug_print("failure; trying to convert to %s\n", out_codeset
);
7023 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7026 if (!test_conv_global_out
&& !test_conv_reply
) {
7028 out_codeset
= CS_INTERNAL
;
7029 debug_print("finally using %s\n", out_codeset
);
7031 g_free(test_conv_global_out
);
7032 g_free(test_conv_reply
);
7033 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7035 codeconv_set_strict(FALSE
);
7040 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7044 cm_return_if_fail(user_data
!= NULL
);
7046 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7047 g_strstrip(address
);
7048 if (*address
!= '\0') {
7049 gchar
*name
= procheader_get_fromname(address
);
7050 extract_address(address
);
7051 #ifndef USE_ALT_ADDRBOOK
7052 addressbook_add_contact(name
, address
, NULL
, NULL
);
7054 debug_print("%s: %s\n", name
, address
);
7055 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7056 debug_print( "addressbook_add_contact - added\n" );
7063 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7065 GtkWidget
*menuitem
;
7068 cm_return_if_fail(menu
!= NULL
);
7069 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7071 menuitem
= gtk_separator_menu_item_new();
7072 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7073 gtk_widget_show(menuitem
);
7075 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7076 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7078 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7079 g_strstrip(address
);
7080 if (*address
== '\0') {
7081 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7084 g_signal_connect(G_OBJECT(menuitem
), "activate",
7085 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7086 gtk_widget_show(menuitem
);
7089 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7092 if (strcmp(header
, "")) {
7093 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7097 void compose_add_extra_header_entries(GtkListStore
*model
)
7101 gchar buf
[BUFFSIZE
];
7104 if (extra_headers
== NULL
) {
7105 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7106 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7107 debug_print("extra headers file not found\n");
7108 goto extra_headers_done
;
7110 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7111 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7112 while (lastc
>= 0 && buf
[lastc
] != ':')
7113 buf
[lastc
--] = '\0';
7114 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7115 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7116 if (custom_header_is_allowed(buf
)) {
7118 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7121 g_message("disallowed extra header line: %s\n", buf
);
7125 g_message("invalid extra header line: %s\n", buf
);
7131 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7132 extra_headers
= g_slist_reverse(extra_headers
);
7134 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7138 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7140 LdapServer
*server
= (LdapServer
*)data
;
7141 gboolean
*enable
= (gboolean
*)user_data
;
7143 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7144 server
->searchFlag
= *enable
;
7148 static void compose_create_header_entry(Compose
*compose
)
7150 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7157 const gchar
*header
= NULL
;
7158 ComposeHeaderEntry
*headerentry
;
7159 gboolean standard_header
= FALSE
;
7160 GtkListStore
*model
;
7163 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7165 /* Combo box model */
7166 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7167 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7169 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7171 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7173 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7174 COMPOSE_NEWSGROUPS
);
7175 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7177 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7178 COMPOSE_FOLLOWUPTO
);
7179 compose_add_extra_header_entries(model
);
7182 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7183 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7184 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7185 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7186 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7187 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7188 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7189 G_CALLBACK(compose_grab_focus_cb
), compose
);
7190 gtk_widget_show(combo
);
7192 /* Putting only the combobox child into focus chain of its parent causes
7193 * the parent to be skipped when changing focus via Tab or Shift+Tab.
7194 * This eliminates need to pres Tab twice in order to really get from the
7195 * combobox to next widget. */
7197 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
7198 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
7201 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
7202 compose
->header_nextrow
, compose
->header_nextrow
+1,
7203 GTK_SHRINK
, GTK_FILL
, 0, 0);
7204 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7205 const gchar
*last_header_entry
= gtk_entry_get_text(
7206 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7208 while (*string
!= NULL
) {
7209 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7210 standard_header
= TRUE
;
7213 if (standard_header
)
7214 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7216 if (!compose
->header_last
|| !standard_header
) {
7217 switch(compose
->account
->protocol
) {
7219 header
= prefs_common_translated_header_name("Newsgroups:");
7222 header
= prefs_common_translated_header_name("To:");
7227 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7229 gtk_editable_set_editable(
7230 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7231 prefs_common
.type_any_header
);
7233 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7234 G_CALLBACK(compose_grab_focus_cb
), compose
);
7236 /* Entry field with cleanup button */
7237 button
= gtk_button_new();
7238 gtk_button_set_image(GTK_BUTTON(button
),
7239 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
7240 gtk_widget_show(button
);
7241 CLAWS_SET_TIP(button
,
7242 _("Delete entry contents"));
7243 entry
= gtk_entry_new();
7244 gtk_widget_show(entry
);
7245 CLAWS_SET_TIP(entry
,
7246 _("Use <tab> to autocomplete from addressbook"));
7247 hbox
= gtk_hbox_new (FALSE
, 0);
7248 gtk_widget_show(hbox
);
7249 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7250 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7251 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
7252 compose
->header_nextrow
, compose
->header_nextrow
+1,
7253 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
7255 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7256 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7258 g_signal_connect(G_OBJECT(entry
), "changed",
7259 G_CALLBACK(compose_headerentry_changed_cb
),
7261 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7262 G_CALLBACK(compose_grab_focus_cb
), compose
);
7264 g_signal_connect(G_OBJECT(button
), "clicked",
7265 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7269 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7270 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7271 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7272 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7273 G_CALLBACK(compose_header_drag_received_cb
),
7275 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7276 G_CALLBACK(compose_drag_drop
),
7278 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7279 G_CALLBACK(compose_entry_popup_extend
),
7283 #ifndef PASSWORD_CRYPTO_OLD
7284 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7285 if (pwd_servers
!= NULL
&& master_passphrase() == NULL
) {
7286 gboolean enable
= FALSE
;
7287 debug_print("Master passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7288 /* Temporarily disable password-protected LDAP servers,
7289 * because user did not provide a master passphrase.
7290 * We can safely enable searchFlag on all servers in this list
7291 * later, since addrindex_get_password_protected_ldap_servers()
7292 * includes servers which have it enabled initially. */
7293 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7294 compose
->passworded_ldap_servers
= pwd_servers
;
7296 #endif /* PASSWORD_CRYPTO_OLD */
7297 #endif /* USE_LDAP */
7299 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7301 headerentry
->compose
= compose
;
7302 headerentry
->combo
= combo
;
7303 headerentry
->entry
= entry
;
7304 headerentry
->button
= button
;
7305 headerentry
->hbox
= hbox
;
7306 headerentry
->headernum
= compose
->header_nextrow
;
7307 headerentry
->type
= PREF_NONE
;
7309 compose
->header_nextrow
++;
7310 compose
->header_last
= headerentry
;
7311 compose
->header_list
=
7312 g_slist_append(compose
->header_list
,
7316 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7317 gchar
*text
, ComposePrefType pref_type
)
7319 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7320 gchar
*tmp
= g_strdup(text
), *email
;
7321 gboolean replyto_hdr
;
7323 replyto_hdr
= (!strcasecmp(header
,
7324 prefs_common_translated_header_name("Reply-To:")) ||
7326 prefs_common_translated_header_name("Followup-To:")) ||
7328 prefs_common_translated_header_name("In-Reply-To:")));
7330 extract_address(tmp
);
7331 email
= g_utf8_strdown(tmp
, -1);
7333 if (replyto_hdr
== FALSE
&&
7334 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7336 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7337 header
, text
, (gint
) pref_type
);
7343 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7344 gtk_entry_set_text(GTK_ENTRY(
7345 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7347 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7348 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7349 last_header
->type
= pref_type
;
7351 if (replyto_hdr
== FALSE
)
7352 g_hash_table_insert(compose
->email_hashtable
, email
,
7353 GUINT_TO_POINTER(1));
7360 static void compose_destroy_headerentry(Compose
*compose
,
7361 ComposeHeaderEntry
*headerentry
)
7363 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7366 extract_address(text
);
7367 email
= g_utf8_strdown(text
, -1);
7368 g_hash_table_remove(compose
->email_hashtable
, email
);
7372 gtk_widget_destroy(headerentry
->combo
);
7373 gtk_widget_destroy(headerentry
->entry
);
7374 gtk_widget_destroy(headerentry
->button
);
7375 gtk_widget_destroy(headerentry
->hbox
);
7376 g_free(headerentry
);
7379 static void compose_remove_header_entries(Compose
*compose
)
7382 for (list
= compose
->header_list
; list
; list
= list
->next
)
7383 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7385 compose
->header_last
= NULL
;
7386 g_slist_free(compose
->header_list
);
7387 compose
->header_list
= NULL
;
7388 compose
->header_nextrow
= 1;
7389 compose_create_header_entry(compose
);
7392 static GtkWidget
*compose_create_header(Compose
*compose
)
7394 GtkWidget
*from_optmenu_hbox
;
7395 GtkWidget
*header_table_main
;
7396 GtkWidget
*header_scrolledwin
;
7397 GtkWidget
*header_table
;
7399 /* parent with account selection and from header */
7400 header_table_main
= gtk_table_new(2, 2, FALSE
);
7401 gtk_widget_show(header_table_main
);
7402 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7404 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7405 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7406 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7408 /* child with header labels and entries */
7409 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7410 gtk_widget_show(header_scrolledwin
);
7411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7413 header_table
= gtk_table_new(2, 2, FALSE
);
7414 gtk_widget_show(header_table
);
7415 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7416 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7417 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7418 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7419 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7421 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7422 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7424 compose
->header_table
= header_table
;
7425 compose
->header_list
= NULL
;
7426 compose
->header_nextrow
= 0;
7428 compose_create_header_entry(compose
);
7430 compose
->table
= NULL
;
7432 return header_table_main
;
7435 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7437 Compose
*compose
= (Compose
*)data
;
7438 GdkEventButton event
;
7441 event
.time
= gtk_get_current_event_time();
7443 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7446 static GtkWidget
*compose_create_attach(Compose
*compose
)
7448 GtkWidget
*attach_scrwin
;
7449 GtkWidget
*attach_clist
;
7451 GtkListStore
*store
;
7452 GtkCellRenderer
*renderer
;
7453 GtkTreeViewColumn
*column
;
7454 GtkTreeSelection
*selection
;
7456 /* attachment list */
7457 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7458 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7459 GTK_POLICY_AUTOMATIC
,
7460 GTK_POLICY_AUTOMATIC
);
7461 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7463 store
= gtk_list_store_new(N_ATTACH_COLS
,
7469 G_TYPE_AUTO_POINTER
,
7471 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7472 (GTK_TREE_MODEL(store
)));
7473 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7474 g_object_unref(store
);
7476 renderer
= gtk_cell_renderer_text_new();
7477 column
= gtk_tree_view_column_new_with_attributes
7478 (_("Mime type"), renderer
, "text",
7479 COL_MIMETYPE
, NULL
);
7480 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7482 renderer
= gtk_cell_renderer_text_new();
7483 column
= gtk_tree_view_column_new_with_attributes
7484 (_("Size"), renderer
, "text",
7486 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7488 renderer
= gtk_cell_renderer_text_new();
7489 column
= gtk_tree_view_column_new_with_attributes
7490 (_("Name"), renderer
, "text",
7492 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7494 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7495 prefs_common
.use_stripes_everywhere
);
7496 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7497 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7499 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7500 G_CALLBACK(attach_selected
), compose
);
7501 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7502 G_CALLBACK(attach_button_pressed
), compose
);
7503 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7504 G_CALLBACK(popup_attach_button_pressed
), compose
);
7505 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7506 G_CALLBACK(attach_key_pressed
), compose
);
7509 gtk_drag_dest_set(attach_clist
,
7510 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7511 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7512 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7513 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7514 G_CALLBACK(compose_attach_drag_received_cb
),
7516 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7517 G_CALLBACK(compose_drag_drop
),
7520 compose
->attach_scrwin
= attach_scrwin
;
7521 compose
->attach_clist
= attach_clist
;
7523 return attach_scrwin
;
7526 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7528 static GtkWidget
*compose_create_others(Compose
*compose
)
7531 GtkWidget
*savemsg_checkbtn
;
7532 GtkWidget
*savemsg_combo
;
7533 GtkWidget
*savemsg_select
;
7536 gchar
*folderidentifier
;
7538 /* Table for settings */
7539 table
= gtk_table_new(3, 1, FALSE
);
7540 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7541 gtk_widget_show(table
);
7542 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7545 /* Save Message to folder */
7546 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7547 gtk_widget_show(savemsg_checkbtn
);
7548 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7549 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7550 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7553 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7554 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7555 compose
->savemsg_combo
= savemsg_combo
;
7556 gtk_widget_show(savemsg_combo
);
7558 if (prefs_common
.compose_save_to_history
)
7559 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7560 prefs_common
.compose_save_to_history
);
7561 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7562 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7563 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7564 G_CALLBACK(compose_grab_focus_cb
), compose
);
7565 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7566 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7567 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7569 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7570 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7571 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7572 (compose
->account
, F_OUTBOX
));
7573 compose_set_save_to(compose
, folderidentifier
);
7574 g_free(folderidentifier
);
7577 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7578 gtk_widget_show(savemsg_select
);
7579 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7580 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7581 G_CALLBACK(compose_savemsg_select_cb
),
7587 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7592 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7593 _("Select folder to save message to"));
7596 path
= folder_item_get_identifier(dest
);
7598 compose_set_save_to(compose
, path
);
7602 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7603 GdkAtom clip
, GtkTextIter
*insert_place
);
7606 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7610 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7612 if (event
->button
== 3) {
7614 GtkTextIter sel_start
, sel_end
;
7615 gboolean stuff_selected
;
7617 /* move the cursor to allow GtkAspell to check the word
7618 * under the mouse */
7619 if (event
->x
&& event
->y
) {
7620 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7621 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7623 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7626 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7627 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7630 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7632 &sel_start
, &sel_end
);
7634 gtk_text_buffer_place_cursor (buffer
, &iter
);
7635 /* reselect stuff */
7637 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7638 gtk_text_buffer_select_range(buffer
,
7639 &sel_start
, &sel_end
);
7641 return FALSE
; /* pass the event so that the right-click goes through */
7644 if (event
->button
== 2) {
7649 /* get the middle-click position to paste at the correct place */
7650 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7651 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7653 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7656 entry_paste_clipboard(compose
, text
,
7657 prefs_common
.linewrap_pastes
,
7658 GDK_SELECTION_PRIMARY
, &iter
);
7666 static void compose_spell_menu_changed(void *data
)
7668 Compose
*compose
= (Compose
*)data
;
7670 GtkWidget
*menuitem
;
7671 GtkWidget
*parent_item
;
7672 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7675 if (compose
->gtkaspell
== NULL
)
7678 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7679 "/Menu/Spelling/Options");
7681 /* setting the submenu removes /Spelling/Options from the factory
7682 * so we need to save it */
7684 if (parent_item
== NULL
) {
7685 parent_item
= compose
->aspell_options_menu
;
7686 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7688 compose
->aspell_options_menu
= parent_item
;
7690 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7692 spell_menu
= g_slist_reverse(spell_menu
);
7693 for (items
= spell_menu
;
7694 items
; items
= items
->next
) {
7695 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7696 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7697 gtk_widget_show(GTK_WIDGET(menuitem
));
7699 g_slist_free(spell_menu
);
7701 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7702 gtk_widget_show(parent_item
);
7705 static void compose_dict_changed(void *data
)
7707 Compose
*compose
= (Compose
*) data
;
7709 if(!compose
->gtkaspell
)
7711 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7714 gtkaspell_highlight_all(compose
->gtkaspell
);
7715 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7719 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7721 Compose
*compose
= (Compose
*)data
;
7722 GdkEventButton event
;
7725 event
.time
= gtk_get_current_event_time();
7729 return text_clicked(compose
->text
, &event
, compose
);
7732 static gboolean compose_force_window_origin
= TRUE
;
7733 static Compose
*compose_create(PrefsAccount
*account
,
7742 GtkWidget
*handlebox
;
7744 GtkWidget
*notebook
;
7746 GtkWidget
*attach_hbox
;
7747 GtkWidget
*attach_lab1
;
7748 GtkWidget
*attach_lab2
;
7753 GtkWidget
*subject_hbox
;
7754 GtkWidget
*subject_frame
;
7755 GtkWidget
*subject_entry
;
7759 GtkWidget
*edit_vbox
;
7760 GtkWidget
*ruler_hbox
;
7762 GtkWidget
*scrolledwin
;
7764 GtkTextBuffer
*buffer
;
7765 GtkClipboard
*clipboard
;
7767 UndoMain
*undostruct
;
7769 GtkWidget
*popupmenu
;
7770 GtkWidget
*tmpl_menu
;
7771 GtkActionGroup
*action_group
= NULL
;
7774 GtkAspell
* gtkaspell
= NULL
;
7777 static GdkGeometry geometry
;
7779 cm_return_val_if_fail(account
!= NULL
, NULL
);
7781 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7782 &default_header_bgcolor
);
7783 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER
],
7784 &default_header_color
);
7786 debug_print("Creating compose window...\n");
7787 compose
= g_new0(Compose
, 1);
7789 compose
->batch
= batch
;
7790 compose
->account
= account
;
7791 compose
->folder
= folder
;
7793 compose
->mutex
= cm_mutex_new();
7794 compose
->set_cursor_pos
= -1;
7796 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7798 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7799 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7800 prefs_common
.compose_height
);
7802 if (!geometry
.max_width
) {
7803 geometry
.max_width
= gdk_screen_width();
7804 geometry
.max_height
= gdk_screen_height();
7807 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7808 &geometry
, GDK_HINT_MAX_SIZE
);
7809 if (!geometry
.min_width
) {
7810 geometry
.min_width
= 600;
7811 geometry
.min_height
= 440;
7813 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7814 &geometry
, GDK_HINT_MIN_SIZE
);
7816 #ifndef GENERIC_UMPC
7817 if (compose_force_window_origin
)
7818 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7819 prefs_common
.compose_y
);
7821 g_signal_connect(G_OBJECT(window
), "delete_event",
7822 G_CALLBACK(compose_delete_cb
), compose
);
7823 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7824 gtk_widget_realize(window
);
7826 gtkut_widget_set_composer_icon(window
);
7828 vbox
= gtk_vbox_new(FALSE
, 0);
7829 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7831 compose
->ui_manager
= gtk_ui_manager_new();
7832 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7833 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7834 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7835 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7836 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7837 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7838 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7839 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7840 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7841 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7843 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7845 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7846 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7848 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7850 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7851 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7852 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7855 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7859 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7860 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7863 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7867 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7870 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7874 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7883 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7885 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7886 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7888 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7890 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7891 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7895 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7896 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7897 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7898 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7906 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7915 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7918 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7919 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7924 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7925 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7926 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7927 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7930 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7933 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7934 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7941 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7946 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7947 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7948 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7949 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7951 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7953 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7954 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7955 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7956 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7957 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7959 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7960 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
)
7961 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
)
7962 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7964 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7966 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7967 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
)
7968 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
)
7970 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7972 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7973 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
)
7974 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7976 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7977 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
)
7978 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7980 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7982 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7983 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
)
7984 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7986 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7987 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7989 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7990 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
)
7991 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
)
7992 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7993 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7995 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7996 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
8002 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
8003 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
8004 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
)
8006 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
8007 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
8008 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
8012 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
8013 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8014 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8015 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8016 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8017 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8020 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8022 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8023 gtk_widget_show_all(menubar
);
8025 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8026 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8028 if (prefs_common
.toolbar_detachable
) {
8029 handlebox
= gtk_handle_box_new();
8031 handlebox
= gtk_hbox_new(FALSE
, 0);
8033 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8035 gtk_widget_realize(handlebox
);
8036 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8039 vbox2
= gtk_vbox_new(FALSE
, 2);
8040 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8041 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8044 notebook
= gtk_notebook_new();
8045 gtk_widget_show(notebook
);
8047 /* header labels and entries */
8048 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8049 compose_create_header(compose
),
8050 gtk_label_new_with_mnemonic(_("Hea_der")));
8051 /* attachment list */
8052 attach_hbox
= gtk_hbox_new(FALSE
, 0);
8053 gtk_widget_show(attach_hbox
);
8055 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8056 gtk_widget_show(attach_lab1
);
8057 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8059 attach_lab2
= gtk_label_new("");
8060 gtk_widget_show(attach_lab2
);
8061 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8063 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8064 compose_create_attach(compose
),
8067 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8068 compose_create_others(compose
),
8069 gtk_label_new_with_mnemonic(_("Othe_rs")));
8072 subject_hbox
= gtk_hbox_new(FALSE
, 0);
8073 gtk_widget_show(subject_hbox
);
8075 subject_frame
= gtk_frame_new(NULL
);
8076 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8077 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8078 gtk_widget_show(subject_frame
);
8080 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
8081 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8082 gtk_widget_show(subject
);
8084 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8085 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8086 gtk_widget_show(label
);
8089 subject_entry
= claws_spell_entry_new();
8091 subject_entry
= gtk_entry_new();
8093 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8094 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8095 G_CALLBACK(compose_grab_focus_cb
), compose
);
8096 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8097 gtk_widget_show(subject_entry
);
8098 compose
->subject_entry
= subject_entry
;
8099 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8101 edit_vbox
= gtk_vbox_new(FALSE
, 0);
8103 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8106 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
8107 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8109 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8110 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8111 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8115 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8116 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8117 GTK_POLICY_AUTOMATIC
,
8118 GTK_POLICY_AUTOMATIC
);
8119 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8121 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8123 text
= gtk_text_view_new();
8124 if (prefs_common
.show_compose_margin
) {
8125 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8126 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8128 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8129 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8130 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8131 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8132 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8134 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8135 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8136 G_CALLBACK(compose_edit_size_alloc
),
8138 g_signal_connect(G_OBJECT(buffer
), "changed",
8139 G_CALLBACK(compose_changed_cb
), compose
);
8140 g_signal_connect(G_OBJECT(text
), "grab_focus",
8141 G_CALLBACK(compose_grab_focus_cb
), compose
);
8142 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8143 G_CALLBACK(text_inserted
), compose
);
8144 g_signal_connect(G_OBJECT(text
), "button_press_event",
8145 G_CALLBACK(text_clicked
), compose
);
8146 g_signal_connect(G_OBJECT(text
), "popup-menu",
8147 G_CALLBACK(compose_popup_menu
), compose
);
8148 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8149 G_CALLBACK(compose_changed_cb
), compose
);
8150 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8151 G_CALLBACK(compose_subject_entry_activated
), compose
);
8154 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8155 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8156 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8157 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8158 G_CALLBACK(compose_insert_drag_received_cb
),
8160 g_signal_connect(G_OBJECT(text
), "drag-drop",
8161 G_CALLBACK(compose_drag_drop
),
8163 g_signal_connect(G_OBJECT(text
), "key-press-event",
8164 G_CALLBACK(completion_set_focus_to_subject
),
8166 gtk_widget_show_all(vbox
);
8168 /* pane between attach clist and text */
8169 paned
= gtk_vpaned_new();
8170 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
8171 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8172 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8173 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8174 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8175 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8177 gtk_widget_show_all(paned
);
8180 if (prefs_common
.textfont
) {
8181 PangoFontDescription
*font_desc
;
8183 font_desc
= pango_font_description_from_string
8184 (prefs_common
.textfont
);
8186 gtk_widget_modify_font(text
, font_desc
);
8187 pango_font_description_free(font_desc
);
8191 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8192 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8193 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8194 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8195 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8196 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8197 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8198 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8200 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8202 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8203 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8204 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8206 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8208 undostruct
= undo_init(text
);
8209 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8212 address_completion_start(window
);
8214 compose
->window
= window
;
8215 compose
->vbox
= vbox
;
8216 compose
->menubar
= menubar
;
8217 compose
->handlebox
= handlebox
;
8219 compose
->vbox2
= vbox2
;
8221 compose
->paned
= paned
;
8223 compose
->attach_label
= attach_lab2
;
8225 compose
->notebook
= notebook
;
8226 compose
->edit_vbox
= edit_vbox
;
8227 compose
->ruler_hbox
= ruler_hbox
;
8228 compose
->ruler
= ruler
;
8229 compose
->scrolledwin
= scrolledwin
;
8230 compose
->text
= text
;
8232 compose
->focused_editable
= NULL
;
8234 compose
->popupmenu
= popupmenu
;
8236 compose
->tmpl_menu
= tmpl_menu
;
8238 compose
->mode
= mode
;
8239 compose
->rmode
= mode
;
8241 compose
->targetinfo
= NULL
;
8242 compose
->replyinfo
= NULL
;
8243 compose
->fwdinfo
= NULL
;
8245 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8246 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8248 compose
->replyto
= NULL
;
8250 compose
->bcc
= NULL
;
8251 compose
->followup_to
= NULL
;
8253 compose
->ml_post
= NULL
;
8255 compose
->inreplyto
= NULL
;
8256 compose
->references
= NULL
;
8257 compose
->msgid
= NULL
;
8258 compose
->boundary
= NULL
;
8260 compose
->autowrap
= prefs_common
.autowrap
;
8261 compose
->autoindent
= prefs_common
.auto_indent
;
8262 compose
->use_signing
= FALSE
;
8263 compose
->use_encryption
= FALSE
;
8264 compose
->privacy_system
= NULL
;
8265 compose
->encdata
= NULL
;
8267 compose
->modified
= FALSE
;
8269 compose
->return_receipt
= FALSE
;
8271 compose
->to_list
= NULL
;
8272 compose
->newsgroup_list
= NULL
;
8274 compose
->undostruct
= undostruct
;
8276 compose
->sig_str
= NULL
;
8278 compose
->exteditor_file
= NULL
;
8279 compose
->exteditor_pid
= -1;
8280 compose
->exteditor_tag
= -1;
8281 compose
->exteditor_socket
= NULL
;
8282 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8284 compose
->folder_update_callback_id
=
8285 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8286 compose_update_folder_hook
,
8287 (gpointer
) compose
);
8290 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8291 if (mode
!= COMPOSE_REDIRECT
) {
8292 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8293 strcmp(prefs_common
.dictionary
, "")) {
8294 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8295 prefs_common
.alt_dictionary
,
8296 conv_get_locale_charset_str(),
8297 prefs_common
.color
[COL_MISSPELLED
],
8298 prefs_common
.check_while_typing
,
8299 prefs_common
.recheck_when_changing_dict
,
8300 prefs_common
.use_alternate
,
8301 prefs_common
.use_both_dicts
,
8302 GTK_TEXT_VIEW(text
),
8303 GTK_WINDOW(compose
->window
),
8304 compose_dict_changed
,
8305 compose_spell_menu_changed
,
8308 alertpanel_error(_("Spell checker could not "
8310 gtkaspell_checkers_strerror());
8311 gtkaspell_checkers_reset_error();
8313 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8317 compose
->gtkaspell
= gtkaspell
;
8318 compose_spell_menu_changed(compose
);
8319 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8322 compose_select_account(compose
, account
, TRUE
);
8324 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8325 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8327 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8328 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8330 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8331 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8333 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8334 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8336 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8337 if (account
->protocol
!= A_NNTP
)
8338 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8339 prefs_common_translated_header_name("To:"));
8341 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8342 prefs_common_translated_header_name("Newsgroups:"));
8344 #ifndef USE_ALT_ADDRBOOK
8345 addressbook_set_target_compose(compose
);
8347 if (mode
!= COMPOSE_REDIRECT
)
8348 compose_set_template_menu(compose
);
8350 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8353 compose_list
= g_list_append(compose_list
, compose
);
8355 if (!prefs_common
.show_ruler
)
8356 gtk_widget_hide(ruler_hbox
);
8358 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8361 compose
->priority
= PRIORITY_NORMAL
;
8362 compose_update_priority_menu_item(compose
);
8364 compose_set_out_encoding(compose
);
8367 compose_update_actions_menu(compose
);
8369 /* Privacy Systems menu */
8370 compose_update_privacy_systems_menu(compose
);
8371 compose_activate_privacy_system(compose
, account
, TRUE
);
8373 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8375 gtk_widget_realize(window
);
8377 gtk_widget_show(window
);
8383 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8388 GtkWidget
*optmenubox
;
8389 GtkWidget
*fromlabel
;
8392 GtkWidget
*from_name
= NULL
;
8394 gint num
= 0, def_menu
= 0;
8396 accounts
= account_get_list();
8397 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8399 optmenubox
= gtk_event_box_new();
8400 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8401 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8403 hbox
= gtk_hbox_new(FALSE
, 4);
8404 from_name
= gtk_entry_new();
8406 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8407 G_CALLBACK(compose_grab_focus_cb
), compose
);
8408 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8409 G_CALLBACK(from_name_activate_cb
), optmenu
);
8411 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8412 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8413 gchar
*name
, *from
= NULL
;
8415 if (ac
== compose
->account
) def_menu
= num
;
8417 name
= g_markup_printf_escaped("<i>%s</i>",
8420 if (ac
== compose
->account
) {
8421 if (ac
->name
&& *ac
->name
) {
8423 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8424 from
= g_strdup_printf("%s <%s>",
8426 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8428 from
= g_strdup_printf("%s",
8430 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8432 if (cur_account
!= compose
->account
) {
8433 gtk_widget_modify_base(
8434 GTK_WIDGET(from_name
),
8435 GTK_STATE_NORMAL
, &default_header_bgcolor
);
8436 gtk_widget_modify_text(
8437 GTK_WIDGET(from_name
),
8438 GTK_STATE_NORMAL
, &default_header_color
);
8441 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8446 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8448 g_signal_connect(G_OBJECT(optmenu
), "changed",
8449 G_CALLBACK(account_activated
),
8451 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8452 G_CALLBACK(compose_entry_popup_extend
),
8455 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8456 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8458 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8459 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8460 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8462 /* Putting only the GtkEntry into focus chain of parent hbox causes
8463 * the account selector combobox next to it to be unreachable when
8464 * navigating widgets in GtkTable with up/down arrow keys.
8465 * Note: gtk_widget_set_can_focus() was not enough. */
8467 l
= g_list_prepend(l
, from_name
);
8468 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), l
);
8471 CLAWS_SET_TIP(optmenubox
,
8472 _("Account to use for this email"));
8473 CLAWS_SET_TIP(from_name
,
8474 _("Sender address to be used"));
8476 compose
->account_combo
= optmenu
;
8477 compose
->from_name
= from_name
;
8482 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8484 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8485 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8486 Compose
*compose
= (Compose
*) data
;
8488 compose
->priority
= value
;
8492 static void compose_reply_change_mode(Compose
*compose
,
8495 gboolean was_modified
= compose
->modified
;
8497 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8499 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8501 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8503 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8505 if (action
== COMPOSE_REPLY_TO_ALL
)
8507 if (action
== COMPOSE_REPLY_TO_SENDER
)
8509 if (action
== COMPOSE_REPLY_TO_LIST
)
8512 compose_remove_header_entries(compose
);
8513 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8514 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8515 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8517 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8518 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8520 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8521 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8522 compose_show_first_last_header(compose
, TRUE
);
8523 compose
->modified
= was_modified
;
8524 compose_set_title(compose
);
8527 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8529 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8530 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8531 Compose
*compose
= (Compose
*) data
;
8534 compose_reply_change_mode(compose
, value
);
8537 static void compose_update_priority_menu_item(Compose
* compose
)
8539 GtkWidget
*menuitem
= NULL
;
8540 switch (compose
->priority
) {
8541 case PRIORITY_HIGHEST
:
8542 menuitem
= gtk_ui_manager_get_widget
8543 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8546 menuitem
= gtk_ui_manager_get_widget
8547 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8549 case PRIORITY_NORMAL
:
8550 menuitem
= gtk_ui_manager_get_widget
8551 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8554 menuitem
= gtk_ui_manager_get_widget
8555 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8557 case PRIORITY_LOWEST
:
8558 menuitem
= gtk_ui_manager_get_widget
8559 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8562 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8565 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8567 Compose
*compose
= (Compose
*) data
;
8569 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8571 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8573 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8576 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8577 g_free(compose
->privacy_system
);
8578 compose
->privacy_system
= NULL
;
8579 g_free(compose
->encdata
);
8580 compose
->encdata
= NULL
;
8581 if (systemid
!= NULL
) {
8582 compose
->privacy_system
= g_strdup(systemid
);
8584 can_sign
= privacy_system_can_sign(systemid
);
8585 can_encrypt
= privacy_system_can_encrypt(systemid
);
8588 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8590 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8592 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8593 gtk_widget_set_sensitive(
8594 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8596 gtk_toggle_tool_button_set_active(
8597 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8598 can_sign
? compose
->use_signing
: FALSE
);
8600 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8601 gtk_widget_set_sensitive(
8602 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8604 gtk_toggle_tool_button_set_active(
8605 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8606 can_encrypt
? compose
->use_encryption
: FALSE
);
8610 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8612 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8613 GtkWidget
*menuitem
= NULL
;
8614 GList
*children
, *amenu
;
8615 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8616 gboolean found
= FALSE
;
8618 if (compose
->privacy_system
!= NULL
) {
8620 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8621 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8622 cm_return_if_fail(menuitem
!= NULL
);
8624 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8627 while (amenu
!= NULL
) {
8628 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8629 if (systemid
!= NULL
) {
8630 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8631 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8632 menuitem
= GTK_WIDGET(amenu
->data
);
8634 can_sign
= privacy_system_can_sign(systemid
);
8635 can_encrypt
= privacy_system_can_encrypt(systemid
);
8639 } else if (strlen(compose
->privacy_system
) == 0 &&
8640 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8641 menuitem
= GTK_WIDGET(amenu
->data
);
8644 can_encrypt
= FALSE
;
8649 amenu
= amenu
->next
;
8651 g_list_free(children
);
8652 if (menuitem
!= NULL
)
8653 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8655 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8656 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8657 "will not be able to sign or encrypt this message."),
8658 compose
->privacy_system
);
8662 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8663 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8664 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8665 gtk_widget_set_sensitive(
8666 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8669 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8670 gtk_widget_set_sensitive(
8671 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8676 static void compose_set_out_encoding(Compose
*compose
)
8678 CharSet out_encoding
;
8679 const gchar
*branch
= NULL
;
8680 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8682 switch(out_encoding
) {
8683 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8684 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8685 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8686 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8687 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8688 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8689 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8690 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8691 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8692 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8693 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8694 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8695 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8696 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8697 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8698 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8699 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8700 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8701 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8702 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8703 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8704 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8705 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8706 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8707 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8708 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8709 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8710 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8711 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8712 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8713 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8714 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8715 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8716 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8718 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8721 static void compose_set_template_menu(Compose
*compose
)
8723 GSList
*tmpl_list
, *cur
;
8727 tmpl_list
= template_get_config();
8729 menu
= gtk_menu_new();
8731 gtk_menu_set_accel_group (GTK_MENU (menu
),
8732 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8733 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8734 Template
*tmpl
= (Template
*)cur
->data
;
8735 gchar
*accel_path
= NULL
;
8736 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8737 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8738 g_signal_connect(G_OBJECT(item
), "activate",
8739 G_CALLBACK(compose_template_activate_cb
),
8741 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8742 gtk_widget_show(item
);
8743 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8744 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8748 gtk_widget_show(menu
);
8749 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8752 void compose_update_actions_menu(Compose
*compose
)
8754 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8757 static void compose_update_privacy_systems_menu(Compose
*compose
)
8759 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8760 GSList
*systems
, *cur
;
8762 GtkWidget
*system_none
;
8764 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8765 GtkWidget
*privacy_menu
= gtk_menu_new();
8767 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8768 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8770 g_signal_connect(G_OBJECT(system_none
), "activate",
8771 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8773 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8774 gtk_widget_show(system_none
);
8776 systems
= privacy_get_system_ids();
8777 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8778 gchar
*systemid
= cur
->data
;
8780 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8781 widget
= gtk_radio_menu_item_new_with_label(group
,
8782 privacy_system_get_name(systemid
));
8783 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8784 g_strdup(systemid
), g_free
);
8785 g_signal_connect(G_OBJECT(widget
), "activate",
8786 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8788 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8789 gtk_widget_show(widget
);
8792 g_slist_free(systems
);
8793 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8794 gtk_widget_show_all(privacy_menu
);
8795 gtk_widget_show_all(privacy_menuitem
);
8798 void compose_reflect_prefs_all(void)
8803 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8804 compose
= (Compose
*)cur
->data
;
8805 compose_set_template_menu(compose
);
8809 void compose_reflect_prefs_pixmap_theme(void)
8814 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8815 compose
= (Compose
*)cur
->data
;
8816 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8820 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8822 const gchar
*qmark
= NULL
;
8824 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8826 switch (compose
->mode
) {
8827 /* use forward-specific quote char */
8828 case COMPOSE_FORWARD
:
8829 case COMPOSE_FORWARD_AS_ATTACH
:
8830 case COMPOSE_FORWARD_INLINE
:
8831 if (compose
->folder
&& compose
->folder
->prefs
&&
8832 compose
->folder
->prefs
->forward_with_format
)
8833 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8834 else if (compose
->account
->forward_with_format
)
8835 qmark
= compose
->account
->forward_quotemark
;
8837 qmark
= prefs_common
.fw_quotemark
;
8840 /* use reply-specific quote char in all other modes */
8842 if (compose
->folder
&& compose
->folder
->prefs
&&
8843 compose
->folder
->prefs
->reply_with_format
)
8844 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8845 else if (compose
->account
->reply_with_format
)
8846 qmark
= compose
->account
->reply_quotemark
;
8848 qmark
= prefs_common
.quotemark
;
8852 if (qmark
== NULL
|| *qmark
== '\0')
8858 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8862 GtkTextBuffer
*buffer
;
8866 gchar
*parsed_str
= NULL
;
8867 gint cursor_pos
= 0;
8868 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8871 /* process the body */
8873 text
= GTK_TEXT_VIEW(compose
->text
);
8874 buffer
= gtk_text_view_get_buffer(text
);
8877 qmark
= compose_quote_char_from_context(compose
);
8879 if (compose
->replyinfo
!= NULL
) {
8882 gtk_text_buffer_set_text(buffer
, "", -1);
8883 mark
= gtk_text_buffer_get_insert(buffer
);
8884 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8886 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8887 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8889 } else if (compose
->fwdinfo
!= NULL
) {
8892 gtk_text_buffer_set_text(buffer
, "", -1);
8893 mark
= gtk_text_buffer_get_insert(buffer
);
8894 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8896 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8897 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8900 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8902 GtkTextIter start
, end
;
8905 gtk_text_buffer_get_start_iter(buffer
, &start
);
8906 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8907 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8909 /* clear the buffer now */
8911 gtk_text_buffer_set_text(buffer
, "", -1);
8913 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8914 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8915 procmsg_msginfo_free( &dummyinfo
);
8921 gtk_text_buffer_set_text(buffer
, "", -1);
8922 mark
= gtk_text_buffer_get_insert(buffer
);
8923 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8926 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8927 compose_insert_sig(compose
, FALSE
);
8929 if (replace
&& parsed_str
) {
8930 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8931 gtk_text_buffer_place_cursor(buffer
, &iter
);
8935 cursor_pos
= quote_fmt_get_cursor_pos();
8936 compose
->set_cursor_pos
= cursor_pos
;
8937 if (cursor_pos
== -1)
8939 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8940 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8941 gtk_text_buffer_place_cursor(buffer
, &iter
);
8944 /* process the other fields */
8946 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8947 compose_template_apply_fields(compose
, tmpl
);
8948 quote_fmt_reset_vartable();
8949 quote_fmtlex_destroy();
8951 compose_changed_cb(NULL
, compose
);
8954 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8955 gtkaspell_highlight_all(compose
->gtkaspell
);
8959 static void compose_template_apply_fields_error(const gchar
*header
)
8964 tr
= g_strdup(C_("'%s' stands for a header name",
8965 "Template '%s' format error."));
8966 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8967 alertpanel_error("%s", text
);
8973 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8975 MsgInfo
* dummyinfo
= NULL
;
8976 MsgInfo
*msginfo
= NULL
;
8979 if (compose
->replyinfo
!= NULL
)
8980 msginfo
= compose
->replyinfo
;
8981 else if (compose
->fwdinfo
!= NULL
)
8982 msginfo
= compose
->fwdinfo
;
8984 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8985 msginfo
= dummyinfo
;
8988 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8990 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8991 compose
->gtkaspell
);
8993 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8995 quote_fmt_scan_string(tmpl
->from
);
8998 buf
= quote_fmt_get_buffer();
9000 compose_template_apply_fields_error("From");
9002 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
9005 quote_fmt_reset_vartable();
9006 quote_fmtlex_destroy();
9009 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
9011 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9012 compose
->gtkaspell
);
9014 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9016 quote_fmt_scan_string(tmpl
->to
);
9019 buf
= quote_fmt_get_buffer();
9021 compose_template_apply_fields_error("To");
9023 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9026 quote_fmt_reset_vartable();
9027 quote_fmtlex_destroy();
9030 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9032 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9033 compose
->gtkaspell
);
9035 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9037 quote_fmt_scan_string(tmpl
->cc
);
9040 buf
= quote_fmt_get_buffer();
9042 compose_template_apply_fields_error("Cc");
9044 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9047 quote_fmt_reset_vartable();
9048 quote_fmtlex_destroy();
9051 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9053 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9054 compose
->gtkaspell
);
9056 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9058 quote_fmt_scan_string(tmpl
->bcc
);
9061 buf
= quote_fmt_get_buffer();
9063 compose_template_apply_fields_error("Bcc");
9065 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9068 quote_fmt_reset_vartable();
9069 quote_fmtlex_destroy();
9072 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9074 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9075 compose
->gtkaspell
);
9077 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9079 quote_fmt_scan_string(tmpl
->replyto
);
9082 buf
= quote_fmt_get_buffer();
9084 compose_template_apply_fields_error("Reply-To");
9086 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9089 quote_fmt_reset_vartable();
9090 quote_fmtlex_destroy();
9093 /* process the subject */
9094 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9096 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9097 compose
->gtkaspell
);
9099 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9101 quote_fmt_scan_string(tmpl
->subject
);
9104 buf
= quote_fmt_get_buffer();
9106 compose_template_apply_fields_error("Subject");
9108 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9111 quote_fmt_reset_vartable();
9112 quote_fmtlex_destroy();
9115 procmsg_msginfo_free( &dummyinfo
);
9118 static void compose_destroy(Compose
*compose
)
9120 GtkAllocation allocation
;
9121 GtkTextBuffer
*buffer
;
9122 GtkClipboard
*clipboard
;
9124 compose_list
= g_list_remove(compose_list
, compose
);
9127 gboolean enable
= TRUE
;
9128 g_slist_foreach(compose
->passworded_ldap_servers
,
9129 _ldap_srv_func
, &enable
);
9130 g_slist_free(compose
->passworded_ldap_servers
);
9133 if (compose
->updating
) {
9134 debug_print("danger, not destroying anything now\n");
9135 compose
->deferred_destroy
= TRUE
;
9139 /* NOTE: address_completion_end() does nothing with the window
9140 * however this may change. */
9141 address_completion_end(compose
->window
);
9143 slist_free_strings_full(compose
->to_list
);
9144 slist_free_strings_full(compose
->newsgroup_list
);
9145 slist_free_strings_full(compose
->header_list
);
9147 slist_free_strings_full(extra_headers
);
9148 extra_headers
= NULL
;
9150 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9152 g_hash_table_destroy(compose
->email_hashtable
);
9154 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9155 compose
->folder_update_callback_id
);
9157 procmsg_msginfo_free(&(compose
->targetinfo
));
9158 procmsg_msginfo_free(&(compose
->replyinfo
));
9159 procmsg_msginfo_free(&(compose
->fwdinfo
));
9161 g_free(compose
->replyto
);
9162 g_free(compose
->cc
);
9163 g_free(compose
->bcc
);
9164 g_free(compose
->newsgroups
);
9165 g_free(compose
->followup_to
);
9167 g_free(compose
->ml_post
);
9169 g_free(compose
->inreplyto
);
9170 g_free(compose
->references
);
9171 g_free(compose
->msgid
);
9172 g_free(compose
->boundary
);
9174 g_free(compose
->redirect_filename
);
9175 if (compose
->undostruct
)
9176 undo_destroy(compose
->undostruct
);
9178 g_free(compose
->sig_str
);
9180 g_free(compose
->exteditor_file
);
9182 g_free(compose
->orig_charset
);
9184 g_free(compose
->privacy_system
);
9185 g_free(compose
->encdata
);
9187 #ifndef USE_ALT_ADDRBOOK
9188 if (addressbook_get_target_compose() == compose
)
9189 addressbook_set_target_compose(NULL
);
9192 if (compose
->gtkaspell
) {
9193 gtkaspell_delete(compose
->gtkaspell
);
9194 compose
->gtkaspell
= NULL
;
9198 if (!compose
->batch
) {
9199 gtk_widget_get_allocation(compose
->window
, &allocation
);
9200 prefs_common
.compose_width
= allocation
.width
;
9201 prefs_common
.compose_height
= allocation
.height
;
9204 if (!gtk_widget_get_parent(compose
->paned
))
9205 gtk_widget_destroy(compose
->paned
);
9206 gtk_widget_destroy(compose
->popupmenu
);
9208 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9209 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9210 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9212 message_search_close(compose
);
9213 gtk_widget_destroy(compose
->window
);
9214 toolbar_destroy(compose
->toolbar
);
9215 g_free(compose
->toolbar
);
9216 cm_mutex_free(compose
->mutex
);
9220 static void compose_attach_info_free(AttachInfo
*ainfo
)
9222 g_free(ainfo
->file
);
9223 g_free(ainfo
->content_type
);
9224 g_free(ainfo
->name
);
9225 g_free(ainfo
->charset
);
9229 static void compose_attach_update_label(Compose
*compose
)
9234 GtkTreeModel
*model
;
9238 if (compose
== NULL
)
9241 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9242 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9243 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9247 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9248 total_size
= ainfo
->size
;
9249 while(gtk_tree_model_iter_next(model
, &iter
)) {
9250 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9251 total_size
+= ainfo
->size
;
9254 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9255 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9259 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9261 Compose
*compose
= (Compose
*)data
;
9262 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9263 GtkTreeSelection
*selection
;
9265 GtkTreeModel
*model
;
9267 selection
= gtk_tree_view_get_selection(tree_view
);
9268 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9269 cm_return_if_fail(sel
);
9271 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9272 GtkTreePath
*path
= cur
->data
;
9273 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9276 gtk_tree_path_free(path
);
9279 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9280 GtkTreeRowReference
*ref
= cur
->data
;
9281 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9284 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9285 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9287 gtk_tree_path_free(path
);
9288 gtk_tree_row_reference_free(ref
);
9292 compose_attach_update_label(compose
);
9295 static struct _AttachProperty
9298 GtkWidget
*mimetype_entry
;
9299 GtkWidget
*encoding_optmenu
;
9300 GtkWidget
*path_entry
;
9301 GtkWidget
*filename_entry
;
9303 GtkWidget
*cancel_btn
;
9306 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9308 gtk_tree_path_free((GtkTreePath
*)ptr
);
9311 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9313 Compose
*compose
= (Compose
*)data
;
9314 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9316 GtkComboBox
*optmenu
;
9317 GtkTreeSelection
*selection
;
9319 GtkTreeModel
*model
;
9322 static gboolean cancelled
;
9324 /* only if one selected */
9325 selection
= gtk_tree_view_get_selection(tree_view
);
9326 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9329 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9330 cm_return_if_fail(sel
);
9332 path
= (GtkTreePath
*) sel
->data
;
9333 gtk_tree_model_get_iter(model
, &iter
, path
);
9334 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9337 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9343 if (!attach_prop
.window
)
9344 compose_attach_property_create(&cancelled
);
9345 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9346 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9347 gtk_widget_show(attach_prop
.window
);
9348 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9349 GTK_WINDOW(compose
->window
));
9351 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9352 if (ainfo
->encoding
== ENC_UNKNOWN
)
9353 combobox_select_by_data(optmenu
, ENC_BASE64
);
9355 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9357 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9358 ainfo
->content_type
? ainfo
->content_type
: "");
9359 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9360 ainfo
->file
? ainfo
->file
: "");
9361 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9362 ainfo
->name
? ainfo
->name
: "");
9365 const gchar
*entry_text
;
9367 gchar
*cnttype
= NULL
;
9374 gtk_widget_hide(attach_prop
.window
);
9375 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9380 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9381 if (*entry_text
!= '\0') {
9384 text
= g_strstrip(g_strdup(entry_text
));
9385 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9386 cnttype
= g_strdup(text
);
9389 alertpanel_error(_("Invalid MIME type."));
9395 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9397 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9398 if (*entry_text
!= '\0') {
9399 if (is_file_exist(entry_text
) &&
9400 (size
= get_file_size(entry_text
)) > 0)
9401 file
= g_strdup(entry_text
);
9404 (_("File doesn't exist or is empty."));
9410 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9411 if (*entry_text
!= '\0') {
9412 g_free(ainfo
->name
);
9413 ainfo
->name
= g_strdup(entry_text
);
9417 g_free(ainfo
->content_type
);
9418 ainfo
->content_type
= cnttype
;
9421 g_free(ainfo
->file
);
9425 ainfo
->size
= (goffset
)size
;
9427 /* update tree store */
9428 text
= to_human_readable(ainfo
->size
);
9429 gtk_tree_model_get_iter(model
, &iter
, path
);
9430 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9431 COL_MIMETYPE
, ainfo
->content_type
,
9433 COL_NAME
, ainfo
->name
,
9434 COL_CHARSET
, ainfo
->charset
,
9440 gtk_tree_path_free(path
);
9443 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9445 label = gtk_label_new(str); \
9446 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9447 GTK_FILL, 0, 0, 0); \
9448 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9450 entry = gtk_entry_new(); \
9451 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9452 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9455 static void compose_attach_property_create(gboolean
*cancelled
)
9461 GtkWidget
*mimetype_entry
;
9464 GtkListStore
*optmenu_menu
;
9465 GtkWidget
*path_entry
;
9466 GtkWidget
*filename_entry
;
9469 GtkWidget
*cancel_btn
;
9470 GList
*mime_type_list
, *strlist
;
9473 debug_print("Creating attach_property window...\n");
9475 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9476 gtk_widget_set_size_request(window
, 480, -1);
9477 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9478 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9479 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9480 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9481 g_signal_connect(G_OBJECT(window
), "delete_event",
9482 G_CALLBACK(attach_property_delete_event
),
9484 g_signal_connect(G_OBJECT(window
), "key_press_event",
9485 G_CALLBACK(attach_property_key_pressed
),
9488 vbox
= gtk_vbox_new(FALSE
, 8);
9489 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9491 table
= gtk_table_new(4, 2, FALSE
);
9492 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9493 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9494 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9496 label
= gtk_label_new(_("MIME type"));
9497 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9499 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9500 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9501 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9502 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9504 /* stuff with list */
9505 mime_type_list
= procmime_get_mime_type_list();
9507 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9508 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9511 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9513 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9516 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9517 (GCompareFunc
)g_strcmp0
);
9520 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9521 mime_type_list
= mime_type_list
->next
) {
9522 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9523 g_free(mime_type_list
->data
);
9525 g_list_free(strlist
);
9526 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9527 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9529 label
= gtk_label_new(_("Encoding"));
9530 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9532 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9534 hbox
= gtk_hbox_new(FALSE
, 0);
9535 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9536 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9538 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9539 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9541 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9542 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9543 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9544 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9545 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9547 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9549 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9550 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9552 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9553 &ok_btn
, GTK_STOCK_OK
,
9555 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9556 gtk_widget_grab_default(ok_btn
);
9558 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9559 G_CALLBACK(attach_property_ok
),
9561 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9562 G_CALLBACK(attach_property_cancel
),
9565 gtk_widget_show_all(vbox
);
9567 attach_prop
.window
= window
;
9568 attach_prop
.mimetype_entry
= mimetype_entry
;
9569 attach_prop
.encoding_optmenu
= optmenu
;
9570 attach_prop
.path_entry
= path_entry
;
9571 attach_prop
.filename_entry
= filename_entry
;
9572 attach_prop
.ok_btn
= ok_btn
;
9573 attach_prop
.cancel_btn
= cancel_btn
;
9576 #undef SET_LABEL_AND_ENTRY
9578 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9584 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9590 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9591 gboolean
*cancelled
)
9599 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9601 gboolean
*cancelled
)
9603 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9607 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9615 static void compose_exec_ext_editor(Compose
*compose
)
9620 GdkNativeWindow socket_wid
= 0;
9624 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9625 G_DIR_SEPARATOR
, compose
);
9627 if (compose_get_ext_editor_uses_socket()) {
9628 /* Only allow one socket */
9629 if (compose
->exteditor_socket
!= NULL
) {
9630 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9631 /* Move the focus off of the socket */
9632 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9637 /* Create the receiving GtkSocket */
9638 socket
= gtk_socket_new ();
9639 g_signal_connect (GTK_OBJECT(socket
), "plug-removed",
9640 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9642 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9643 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9644 /* Realize the socket so that we can use its ID */
9645 gtk_widget_realize(socket
);
9646 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9647 compose
->exteditor_socket
= socket
;
9650 if (pipe(pipe_fds
) < 0) {
9656 if ((pid
= fork()) < 0) {
9663 /* close the write side of the pipe */
9666 compose
->exteditor_file
= g_strdup(tmp
);
9667 compose
->exteditor_pid
= pid
;
9669 compose_set_ext_editor_sensitive(compose
, FALSE
);
9672 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
9674 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
9676 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
9680 } else { /* process-monitoring process */
9686 /* close the read side of the pipe */
9689 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9690 fd_write_all(pipe_fds
[1], "2\n", 2);
9694 pid_ed
= compose_exec_ext_editor_real(tmp
, socket_wid
);
9696 fd_write_all(pipe_fds
[1], "1\n", 2);
9700 /* wait until editor is terminated */
9701 waitpid(pid_ed
, NULL
, 0);
9703 fd_write_all(pipe_fds
[1], "0\n", 2);
9710 #endif /* G_OS_UNIX */
9713 static gboolean
compose_can_autosave(Compose
*compose
)
9715 if (compose
->privacy_system
&& compose
->use_encryption
)
9716 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9718 return prefs_common
.autosave
;
9722 static gboolean
compose_get_ext_editor_cmd_valid()
9724 gboolean has_s
= FALSE
;
9725 gboolean has_w
= FALSE
;
9726 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9729 while ((p
= strchr(p
, '%'))) {
9735 } else if (*p
== 'w') {
9746 static gint
compose_exec_ext_editor_real(const gchar
*file
, GdkNativeWindow socket_wid
)
9753 cm_return_val_if_fail(file
!= NULL
, -1);
9755 if ((pid
= fork()) < 0) {
9760 if (pid
!= 0) return pid
;
9762 /* grandchild process */
9764 if (setpgid(0, getppid()))
9767 if (compose_get_ext_editor_cmd_valid()) {
9768 if (compose_get_ext_editor_uses_socket()) {
9769 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9770 s
= strstr(p
, "%w");
9772 if (strstr(p
, "%s") < s
)
9773 buf
= g_strdup_printf(p
, file
, socket_wid
);
9775 buf
= g_strdup_printf(p
, socket_wid
, file
);
9778 buf
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), file
);
9781 if (prefs_common_get_ext_editor_cmd())
9782 g_warning("External editor command-line is invalid: '%s'",
9783 prefs_common_get_ext_editor_cmd());
9784 buf
= g_strdup_printf(DEFAULT_EDITOR_CMD
, file
);
9787 cmdline
= strsplit_with_quote(buf
, " ", 0);
9789 execvp(cmdline
[0], cmdline
);
9792 g_strfreev(cmdline
);
9797 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9799 pid_t pgid
= compose
->exteditor_pid
* -1;
9802 ret
= kill(pgid
, 0);
9804 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
9808 msg
= g_strdup_printf
9809 (_("The external editor is still working.\n"
9810 "Force terminating the process?\n"
9811 "process group id: %d"), -pgid
);
9812 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
9813 NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9818 if (val
== G_ALERTALTERNATE
) {
9819 g_source_remove(compose
->exteditor_tag
);
9820 g_io_channel_shutdown(compose
->exteditor_ch
,
9822 g_io_channel_unref(compose
->exteditor_ch
);
9824 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
9825 waitpid(compose
->exteditor_pid
, NULL
, 0);
9827 g_warning("Terminated process group id: %d. "
9828 "Temporary file: %s", -pgid
, compose
->exteditor_file
);
9830 compose_set_ext_editor_sensitive(compose
, TRUE
);
9832 g_free(compose
->exteditor_file
);
9833 compose
->exteditor_file
= NULL
;
9834 compose
->exteditor_pid
= -1;
9835 compose
->exteditor_ch
= NULL
;
9836 compose
->exteditor_tag
= -1;
9844 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9848 Compose
*compose
= (Compose
*)data
;
9851 debug_print("Compose: input from monitoring process\n");
9853 if (g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
) != G_IO_STATUS_NORMAL
) {
9858 g_io_channel_shutdown(source
, FALSE
, NULL
);
9859 g_io_channel_unref(source
);
9861 waitpid(compose
->exteditor_pid
, NULL
, 0);
9863 if (buf
[0] == '0') { /* success */
9864 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9865 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9866 GtkTextIter start
, end
;
9869 gtk_text_buffer_set_text(buffer
, "", -1);
9870 compose_insert_file(compose
, compose
->exteditor_file
);
9871 compose_changed_cb(NULL
, compose
);
9873 /* Check if we should save the draft or not */
9874 if (compose_can_autosave(compose
))
9875 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9877 if (claws_unlink(compose
->exteditor_file
) < 0)
9878 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9880 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9881 gtk_text_buffer_get_start_iter(buffer
, &start
);
9882 gtk_text_buffer_get_end_iter(buffer
, &end
);
9883 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9884 if (chars
&& strlen(chars
) > 0)
9885 compose
->modified
= TRUE
;
9887 } else if (buf
[0] == '1') { /* failed */
9888 g_warning("Couldn't exec external editor");
9889 if (claws_unlink(compose
->exteditor_file
) < 0)
9890 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9891 } else if (buf
[0] == '2') {
9892 g_warning("Couldn't write to file");
9893 } else if (buf
[0] == '3') {
9894 g_warning("Pipe read failed");
9897 compose_set_ext_editor_sensitive(compose
, TRUE
);
9899 g_free(compose
->exteditor_file
);
9900 compose
->exteditor_file
= NULL
;
9901 compose
->exteditor_pid
= -1;
9902 compose
->exteditor_ch
= NULL
;
9903 compose
->exteditor_tag
= -1;
9904 if (compose
->exteditor_socket
) {
9905 gtk_widget_destroy(compose
->exteditor_socket
);
9906 compose
->exteditor_socket
= NULL
;
9913 static char *ext_editor_menu_entries
[] = {
9914 "Menu/Message/Send",
9915 "Menu/Message/SendLater",
9916 "Menu/Message/InsertFile",
9917 "Menu/Message/InsertSig",
9918 "Menu/Message/ReplaceSig",
9919 "Menu/Message/Save",
9920 "Menu/Message/Print",
9925 "Menu/Tools/ShowRuler",
9926 "Menu/Tools/Actions",
9931 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9936 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9937 cm_menu_set_sensitive_full(compose
->ui_manager
,
9938 ext_editor_menu_entries
[i
], sensitive
);
9941 if (compose_get_ext_editor_uses_socket()) {
9943 if (compose
->exteditor_socket
)
9944 gtk_widget_hide(compose
->exteditor_socket
);
9945 gtk_widget_show(compose
->scrolledwin
);
9946 if (prefs_common
.show_ruler
)
9947 gtk_widget_show(compose
->ruler_hbox
);
9948 /* Fix the focus, as it doesn't go anywhere when the
9949 * socket is hidden or destroyed */
9950 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9952 g_assert (compose
->exteditor_socket
!= NULL
);
9953 /* Fix the focus, as it doesn't go anywhere when the
9954 * edit box is hidden */
9955 if (gtk_widget_is_focus(compose
->text
))
9956 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9957 gtk_widget_hide(compose
->scrolledwin
);
9958 gtk_widget_hide(compose
->ruler_hbox
);
9959 gtk_widget_show(compose
->exteditor_socket
);
9962 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9964 if (compose
->toolbar
->send_btn
)
9965 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9966 if (compose
->toolbar
->sendl_btn
)
9967 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9968 if (compose
->toolbar
->draft_btn
)
9969 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9970 if (compose
->toolbar
->insert_btn
)
9971 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9972 if (compose
->toolbar
->sig_btn
)
9973 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9974 if (compose
->toolbar
->exteditor_btn
)
9975 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9976 if (compose
->toolbar
->linewrap_current_btn
)
9977 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9978 if (compose
->toolbar
->linewrap_all_btn
)
9979 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9982 static gboolean
compose_get_ext_editor_uses_socket()
9984 return (prefs_common_get_ext_editor_cmd() &&
9985 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9988 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9990 compose
->exteditor_socket
= NULL
;
9991 /* returning FALSE allows destruction of the socket */
9994 #endif /* G_OS_UNIX */
9997 * compose_undo_state_changed:
9999 * Change the sensivity of the menuentries undo and redo
10001 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
10002 gint redo_state
, gpointer data
)
10004 Compose
*compose
= (Compose
*)data
;
10006 switch (undo_state
) {
10007 case UNDO_STATE_TRUE
:
10008 if (!undostruct
->undo_state
) {
10009 undostruct
->undo_state
= TRUE
;
10010 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
10013 case UNDO_STATE_FALSE
:
10014 if (undostruct
->undo_state
) {
10015 undostruct
->undo_state
= FALSE
;
10016 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
10019 case UNDO_STATE_UNCHANGED
:
10021 case UNDO_STATE_REFRESH
:
10022 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
10025 g_warning("Undo state not recognized");
10029 switch (redo_state
) {
10030 case UNDO_STATE_TRUE
:
10031 if (!undostruct
->redo_state
) {
10032 undostruct
->redo_state
= TRUE
;
10033 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
10036 case UNDO_STATE_FALSE
:
10037 if (undostruct
->redo_state
) {
10038 undostruct
->redo_state
= FALSE
;
10039 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
10042 case UNDO_STATE_UNCHANGED
:
10044 case UNDO_STATE_REFRESH
:
10045 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10048 g_warning("Redo state not recognized");
10053 /* callback functions */
10055 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10056 GtkAllocation
*allocation
,
10059 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10062 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10063 * includes "non-client" (windows-izm) in calculation, so this calculation
10064 * may not be accurate.
10066 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10067 GtkAllocation
*allocation
,
10068 GtkSHRuler
*shruler
)
10070 if (prefs_common
.show_ruler
) {
10071 gint char_width
= 0, char_height
= 0;
10072 gint line_width_in_chars
;
10074 gtkut_get_font_size(GTK_WIDGET(widget
),
10075 &char_width
, &char_height
);
10076 line_width_in_chars
=
10077 (allocation
->width
- allocation
->x
) / char_width
;
10079 /* got the maximum */
10080 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10081 0.0, line_width_in_chars
, 0);
10090 ComposePrefType type
;
10091 gboolean entry_marked
;
10092 } HeaderEntryState
;
10094 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10096 Compose
*compose
= (Compose
*)data
;
10099 gchar
*folderidentifier
;
10100 gint account_id
= 0;
10101 GtkTreeModel
*menu
;
10103 GSList
*list
, *saved_list
= NULL
;
10104 HeaderEntryState
*state
;
10106 /* Get ID of active account in the combo box */
10107 menu
= gtk_combo_box_get_model(optmenu
);
10108 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10109 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10111 ac
= account_find_from_id(account_id
);
10112 cm_return_if_fail(ac
!= NULL
);
10114 if (ac
!= compose
->account
) {
10115 compose_select_account(compose
, ac
, FALSE
);
10117 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10118 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10120 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10121 compose_destroy_headerentry(compose
, hentry
);
10124 state
= g_malloc0(sizeof(HeaderEntryState
));
10125 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10126 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10127 state
->entry
= gtk_editable_get_chars(
10128 GTK_EDITABLE(hentry
->entry
), 0, -1);
10129 state
->type
= hentry
->type
;
10131 saved_list
= g_slist_append(saved_list
, state
);
10132 compose_destroy_headerentry(compose
, hentry
);
10135 compose
->header_last
= NULL
;
10136 g_slist_free(compose
->header_list
);
10137 compose
->header_list
= NULL
;
10138 compose
->header_nextrow
= 1;
10139 compose_create_header_entry(compose
);
10141 if (ac
->set_autocc
&& ac
->auto_cc
)
10142 compose_entry_append(compose
, ac
->auto_cc
,
10143 COMPOSE_CC
, PREF_ACCOUNT
);
10144 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10145 compose_entry_append(compose
, ac
->auto_bcc
,
10146 COMPOSE_BCC
, PREF_ACCOUNT
);
10147 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10148 compose_entry_append(compose
, ac
->auto_replyto
,
10149 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10151 for (list
= saved_list
; list
; list
= list
->next
) {
10152 state
= (HeaderEntryState
*) list
->data
;
10154 compose_add_header_entry(compose
, state
->header
,
10155 state
->entry
, state
->type
);
10157 g_free(state
->header
);
10158 g_free(state
->entry
);
10161 g_slist_free(saved_list
);
10163 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10164 (ac
->protocol
== A_NNTP
) ?
10165 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10168 /* Set message save folder */
10169 compose_set_save_to(compose
, NULL
);
10170 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10172 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10173 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10174 compose_set_save_to(compose
, folderidentifier
);
10175 g_free(folderidentifier
);
10176 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10177 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10180 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10181 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10182 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10183 (compose
->account
, F_OUTBOX
));
10184 compose_set_save_to(compose
, folderidentifier
);
10185 g_free(folderidentifier
);
10189 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10190 GtkTreeViewColumn
*column
, Compose
*compose
)
10192 compose_attach_property(NULL
, compose
);
10195 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10198 Compose
*compose
= (Compose
*)data
;
10199 GtkTreeSelection
*attach_selection
;
10200 gint attach_nr_selected
;
10203 if (!event
) return FALSE
;
10205 if (event
->button
== 3) {
10206 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10207 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10209 /* If no rows, or just one row is selected, right-click should
10210 * open menu relevant to the row being right-clicked on. We
10211 * achieve that by selecting the clicked row first. If more
10212 * than one row is selected, we shouldn't modify the selection,
10213 * as user may want to remove selected rows (attachments). */
10214 if (attach_nr_selected
< 2) {
10215 gtk_tree_selection_unselect_all(attach_selection
);
10216 attach_nr_selected
= 0;
10217 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10218 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10219 if (path
!= NULL
) {
10220 gtk_tree_selection_select_path(attach_selection
, path
);
10221 gtk_tree_path_free(path
);
10222 attach_nr_selected
++;
10226 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10227 /* Properties menu item makes no sense with more than one row
10228 * selected, the properties dialog can only edit one attachment. */
10229 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10231 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
10232 NULL
, NULL
, event
->button
, event
->time
);
10239 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10242 Compose
*compose
= (Compose
*)data
;
10244 if (!event
) return FALSE
;
10246 switch (event
->keyval
) {
10247 case GDK_KEY_Delete
:
10248 compose_attach_remove_selected(NULL
, compose
);
10254 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10256 toolbar_comp_set_sensitive(compose
, allow
);
10257 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10258 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10260 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10262 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10263 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10264 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10266 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10270 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10272 Compose
*compose
= (Compose
*)data
;
10275 if (compose
->exteditor_tag
!= -1) {
10276 debug_print("ignoring send: external editor still open\n");
10280 if (prefs_common
.work_offline
&&
10281 !inc_offline_should_override(TRUE
,
10282 _("Claws Mail needs network access in order "
10283 "to send this email.")))
10286 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10287 g_source_remove(compose
->draft_timeout_tag
);
10288 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10291 compose_send(compose
);
10294 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10296 Compose
*compose
= (Compose
*)data
;
10297 ComposeQueueResult val
;
10300 compose_allow_user_actions(compose
, FALSE
);
10301 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10302 compose_allow_user_actions(compose
, TRUE
);
10305 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10306 compose_close(compose
);
10308 _display_queue_error(val
);
10311 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10314 #define DRAFTED_AT_EXIT "drafted_at_exit"
10315 static void compose_register_draft(MsgInfo
*info
)
10317 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10318 DRAFTED_AT_EXIT
, NULL
);
10319 FILE *fp
= claws_fopen(filepath
, "ab");
10322 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
10330 gboolean
compose_draft (gpointer data
, guint action
)
10332 Compose
*compose
= (Compose
*)data
;
10337 MsgFlags flag
= {0, 0};
10338 static gboolean lock
= FALSE
;
10339 MsgInfo
*newmsginfo
;
10341 gboolean target_locked
= FALSE
;
10342 gboolean err
= FALSE
;
10344 if (lock
) return FALSE
;
10346 if (compose
->sending
)
10349 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10350 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10352 if (!g_mutex_trylock(compose
->mutex
)) {
10353 /* we don't want to lock the mutex once it's available,
10354 * because as the only other part of compose.c locking
10355 * it is compose_close - which means once unlocked,
10356 * the compose struct will be freed */
10357 debug_print("couldn't lock mutex, probably sending\n");
10363 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10364 G_DIR_SEPARATOR
, compose
);
10365 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10366 FILE_OP_ERROR(tmp
, "claws_fopen");
10370 /* chmod for security */
10371 if (change_file_mode_rw(fp
, tmp
) < 0) {
10372 FILE_OP_ERROR(tmp
, "chmod");
10373 g_warning("can't change file mode");
10376 /* Save draft infos */
10377 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10378 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10380 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10381 gchar
*savefolderid
;
10383 savefolderid
= compose_get_save_to(compose
);
10384 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10385 g_free(savefolderid
);
10387 if (compose
->return_receipt
) {
10388 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10390 if (compose
->privacy_system
) {
10391 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10392 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10393 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10396 /* Message-ID of message replying to */
10397 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10398 gchar
*folderid
= NULL
;
10400 if (compose
->replyinfo
->folder
)
10401 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10402 if (folderid
== NULL
)
10403 folderid
= g_strdup("NULL");
10405 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10408 /* Message-ID of message forwarding to */
10409 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10410 gchar
*folderid
= NULL
;
10412 if (compose
->fwdinfo
->folder
)
10413 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10414 if (folderid
== NULL
)
10415 folderid
= g_strdup("NULL");
10417 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10421 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10422 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10424 sheaders
= compose_get_manual_headers_info(compose
);
10425 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10428 /* end of headers */
10429 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10436 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10440 if (claws_safe_fclose(fp
) == EOF
) {
10444 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10445 if (compose
->targetinfo
) {
10446 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10448 flag
.perm_flags
|= MSG_LOCKED
;
10450 flag
.tmp_flags
= MSG_DRAFT
;
10452 folder_item_scan(draft
);
10453 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10454 MsgInfo
*tmpinfo
= NULL
;
10455 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10456 if (compose
->msgid
) {
10457 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10460 msgnum
= tmpinfo
->msgnum
;
10461 procmsg_msginfo_free(&tmpinfo
);
10462 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10464 debug_print("didn't get draft msgnum after scanning\n");
10467 debug_print("got draft msgnum %d from adding\n", msgnum
);
10473 if (action
!= COMPOSE_AUTO_SAVE
) {
10474 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10475 alertpanel_error(_("Could not save draft."));
10478 gtkut_window_popup(compose
->window
);
10479 val
= alertpanel_full(_("Could not save draft"),
10480 _("Could not save draft.\n"
10481 "Do you want to cancel exit or discard this email?"),
10482 _("_Cancel exit"), _("_Discard email"), NULL
, ALERTFOCUS_FIRST
,
10483 FALSE
, NULL
, ALERT_QUESTION
);
10484 if (val
== G_ALERTALTERNATE
) {
10486 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10487 compose_close(compose
);
10491 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10500 if (compose
->mode
== COMPOSE_REEDIT
) {
10501 compose_remove_reedit_target(compose
, TRUE
);
10504 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10507 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10509 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10511 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10512 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10513 procmsg_msginfo_set_flags(newmsginfo
, 0,
10514 MSG_HAS_ATTACHMENT
);
10516 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10517 compose_register_draft(newmsginfo
);
10519 procmsg_msginfo_free(&newmsginfo
);
10522 folder_item_scan(draft
);
10524 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10526 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10527 compose_close(compose
);
10534 GError
*error
= NULL
;
10539 goffset size
, mtime
;
10541 path
= folder_item_fetch_msg(draft
, msgnum
);
10542 if (path
== NULL
) {
10543 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10547 f
= g_file_new_for_path(path
);
10548 fi
= g_file_query_info(f
, "standard::size,time::modified",
10549 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10550 if (error
!= NULL
) {
10551 debug_print("couldn't query file info for '%s': %s\n",
10552 path
, error
->message
);
10553 g_error_free(error
);
10558 size
= g_file_info_get_size(fi
);
10559 g_file_info_get_modification_time(fi
, &tv
);
10561 g_object_unref(fi
);
10564 if (g_stat(path
, &s
) < 0) {
10565 FILE_OP_ERROR(path
, "stat");
10570 mtime
= s
.st_mtime
;
10574 procmsg_msginfo_free(&(compose
->targetinfo
));
10575 compose
->targetinfo
= procmsg_msginfo_new();
10576 compose
->targetinfo
->msgnum
= msgnum
;
10577 compose
->targetinfo
->size
= size
;
10578 compose
->targetinfo
->mtime
= mtime
;
10579 compose
->targetinfo
->folder
= draft
;
10581 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10582 compose
->mode
= COMPOSE_REEDIT
;
10584 if (action
== COMPOSE_AUTO_SAVE
) {
10585 compose
->autosaved_draft
= compose
->targetinfo
;
10587 compose
->modified
= FALSE
;
10588 compose_set_title(compose
);
10592 g_mutex_unlock(compose
->mutex
);
10596 void compose_clear_exit_drafts(void)
10598 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10599 DRAFTED_AT_EXIT
, NULL
);
10600 if (is_file_exist(filepath
))
10601 claws_unlink(filepath
);
10606 void compose_reopen_exit_drafts(void)
10608 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10609 DRAFTED_AT_EXIT
, NULL
);
10610 FILE *fp
= claws_fopen(filepath
, "rb");
10614 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10615 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10616 const gchar
*folder
= parts
[0];
10617 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10619 if (folder
&& *folder
&& msgnum
> -1) {
10620 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10621 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10623 compose_reedit(info
, FALSE
);
10630 compose_clear_exit_drafts();
10633 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10635 Compose
*compose
= (Compose
*)data
;
10636 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10637 compose
->rmode
= COMPOSE_REEDIT
;
10640 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10642 if (compose
&& file_list
) {
10645 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10646 gchar
*file
= (gchar
*) tmp
->data
;
10647 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10648 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10649 compose_changed_cb(NULL
, compose
);
10654 g_free(utf8_filename
);
10659 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10661 Compose
*compose
= (Compose
*)data
;
10664 if (compose
->redirect_filename
!= NULL
)
10667 /* Set focus_window properly, in case we were called via popup menu,
10668 * which unsets it (via focus_out_event callback on compose window). */
10669 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10671 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10674 compose_attach_from_list(compose
, file_list
, TRUE
);
10675 g_list_free(file_list
);
10679 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10681 Compose
*compose
= (Compose
*)data
;
10683 gint files_inserted
= 0;
10685 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10690 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10691 gchar
*file
= (gchar
*) tmp
->data
;
10692 gchar
*filedup
= g_strdup(file
);
10693 gchar
*shortfile
= g_path_get_basename(filedup
);
10694 ComposeInsertResult res
;
10695 /* insert the file if the file is short or if the user confirmed that
10696 he/she wants to insert the large file */
10697 res
= compose_insert_file(compose
, file
);
10698 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10699 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10700 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10701 alertpanel_error(_("File '%s' contained invalid characters\n"
10702 "for the current encoding, insertion may be incorrect."),
10704 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10711 g_list_free(file_list
);
10715 if (files_inserted
> 0 && compose
->gtkaspell
&&
10716 compose
->gtkaspell
->check_while_typing
)
10717 gtkaspell_highlight_all(compose
->gtkaspell
);
10721 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10723 Compose
*compose
= (Compose
*)data
;
10725 compose_insert_sig(compose
, FALSE
);
10728 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10730 Compose
*compose
= (Compose
*)data
;
10732 compose_insert_sig(compose
, TRUE
);
10735 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10739 Compose
*compose
= (Compose
*)data
;
10741 gtkut_widget_get_uposition(widget
, &x
, &y
);
10742 if (!compose
->batch
) {
10743 prefs_common
.compose_x
= x
;
10744 prefs_common
.compose_y
= y
;
10746 if (compose
->sending
|| compose
->updating
)
10748 compose_close_cb(NULL
, compose
);
10752 void compose_close_toolbar(Compose
*compose
)
10754 compose_close_cb(NULL
, compose
);
10757 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10759 Compose
*compose
= (Compose
*)data
;
10763 if (compose
->exteditor_tag
!= -1) {
10764 if (!compose_ext_editor_kill(compose
))
10769 if (compose
->modified
) {
10770 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10771 if (!g_mutex_trylock(compose
->mutex
)) {
10772 /* we don't want to lock the mutex once it's available,
10773 * because as the only other part of compose.c locking
10774 * it is compose_close - which means once unlocked,
10775 * the compose struct will be freed */
10776 debug_print("couldn't lock mutex, probably sending\n");
10779 if (!reedit
|| compose
->folder
->stype
== F_DRAFT
) {
10780 val
= alertpanel(_("Discard message"),
10781 _("This message has been modified. Discard it?"),
10782 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10785 val
= alertpanel(_("Save changes"),
10786 _("This message has been modified. Save the latest changes?"),
10787 _("_Don't save"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10788 ALERTFOCUS_SECOND
);
10790 g_mutex_unlock(compose
->mutex
);
10792 case G_ALERTDEFAULT
:
10793 if (compose_can_autosave(compose
) && !reedit
)
10794 compose_remove_draft(compose
);
10796 case G_ALERTALTERNATE
:
10797 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10804 compose_close(compose
);
10807 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10809 Compose
*compose
= (Compose
*) data
;
10811 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10812 if (compose
->targetinfo
)
10813 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10816 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10818 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10819 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10820 Compose
*compose
= (Compose
*) data
;
10823 compose
->out_encoding
= (CharSet
)value
;
10826 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10828 Compose
*compose
= (Compose
*)data
;
10830 #ifndef USE_ALT_ADDRBOOK
10831 addressbook_open(compose
);
10833 GError
* error
= NULL
;
10834 addressbook_connect_signals(compose
);
10835 addressbook_dbus_open(TRUE
, &error
);
10837 g_warning("%s", error
->message
);
10838 g_error_free(error
);
10843 static void about_show_cb(GtkAction
*action
, gpointer data
)
10848 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10850 Compose
*compose
= (Compose
*)data
;
10855 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10856 cm_return_if_fail(tmpl
!= NULL
);
10858 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10860 val
= alertpanel(_("Apply template"), msg
,
10861 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
, ALERTFOCUS_FIRST
);
10864 if (val
== G_ALERTDEFAULT
)
10865 compose_template_apply(compose
, tmpl
, TRUE
);
10866 else if (val
== G_ALERTALTERNATE
)
10867 compose_template_apply(compose
, tmpl
, FALSE
);
10870 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10872 Compose
*compose
= (Compose
*)data
;
10875 if (compose
->exteditor_tag
!= -1) {
10876 debug_print("ignoring open external editor: external editor still open\n");
10880 compose_exec_ext_editor(compose
);
10883 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10885 Compose
*compose
= (Compose
*)data
;
10886 gboolean prev_autowrap
= compose
->autowrap
;
10888 compose
->autowrap
= FALSE
;
10889 undo_undo(compose
->undostruct
);
10890 compose
->autowrap
= prev_autowrap
;
10893 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10895 Compose
*compose
= (Compose
*)data
;
10896 gboolean prev_autowrap
= compose
->autowrap
;
10898 compose
->autowrap
= FALSE
;
10899 undo_redo(compose
->undostruct
);
10900 compose
->autowrap
= prev_autowrap
;
10903 static void entry_cut_clipboard(GtkWidget
*entry
)
10905 if (GTK_IS_EDITABLE(entry
))
10906 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10907 else if (GTK_IS_TEXT_VIEW(entry
))
10908 gtk_text_buffer_cut_clipboard(
10909 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10910 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10914 static void entry_copy_clipboard(GtkWidget
*entry
)
10916 if (GTK_IS_EDITABLE(entry
))
10917 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10918 else if (GTK_IS_TEXT_VIEW(entry
))
10919 gtk_text_buffer_copy_clipboard(
10920 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10921 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10924 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10925 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10927 if (GTK_IS_TEXT_VIEW(entry
)) {
10928 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10929 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10930 GtkTextIter start_iter
, end_iter
;
10932 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10934 if (contents
== NULL
)
10937 /* we shouldn't delete the selection when middle-click-pasting, or we
10938 * can't mid-click-paste our own selection */
10939 if (clip
!= GDK_SELECTION_PRIMARY
) {
10940 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10941 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10944 if (insert_place
== NULL
) {
10945 /* if insert_place isn't specified, insert at the cursor.
10946 * used for Ctrl-V pasting */
10947 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10948 start
= gtk_text_iter_get_offset(&start_iter
);
10949 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10951 /* if insert_place is specified, paste here.
10952 * used for mid-click-pasting */
10953 start
= gtk_text_iter_get_offset(insert_place
);
10954 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10955 if (prefs_common
.primary_paste_unselects
)
10956 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10960 /* paste unwrapped: mark the paste so it's not wrapped later */
10961 end
= start
+ strlen(contents
);
10962 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10963 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10964 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10965 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10966 /* rewrap paragraph now (after a mid-click-paste) */
10967 mark_start
= gtk_text_buffer_get_insert(buffer
);
10968 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10969 gtk_text_iter_backward_char(&start_iter
);
10970 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10972 } else if (GTK_IS_EDITABLE(entry
))
10973 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10975 compose
->modified
= TRUE
;
10978 static void entry_allsel(GtkWidget
*entry
)
10980 if (GTK_IS_EDITABLE(entry
))
10981 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10982 else if (GTK_IS_TEXT_VIEW(entry
)) {
10983 GtkTextIter startiter
, enditer
;
10984 GtkTextBuffer
*textbuf
;
10986 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10987 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10988 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10990 gtk_text_buffer_move_mark_by_name(textbuf
,
10991 "selection_bound", &startiter
);
10992 gtk_text_buffer_move_mark_by_name(textbuf
,
10993 "insert", &enditer
);
10997 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10999 Compose
*compose
= (Compose
*)data
;
11000 if (compose
->focused_editable
11001 #ifndef GENERIC_UMPC
11002 && gtk_widget_has_focus(compose
->focused_editable
)
11005 entry_cut_clipboard(compose
->focused_editable
);
11008 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
11010 Compose
*compose
= (Compose
*)data
;
11011 if (compose
->focused_editable
11012 #ifndef GENERIC_UMPC
11013 && gtk_widget_has_focus(compose
->focused_editable
)
11016 entry_copy_clipboard(compose
->focused_editable
);
11019 static void compose_paste_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
,
11031 prefs_common
.linewrap_pastes
,
11032 GDK_SELECTION_CLIPBOARD
, NULL
);
11037 #ifndef GENERIC_UMPC
11038 gtk_widget_has_focus(compose
->text
) &&
11040 compose
->gtkaspell
&&
11041 compose
->gtkaspell
->check_while_typing
)
11042 gtkaspell_highlight_all(compose
->gtkaspell
);
11046 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11048 Compose
*compose
= (Compose
*)data
;
11049 gint wrap_quote
= prefs_common
.linewrap_quote
;
11050 if (compose
->focused_editable
11051 #ifndef GENERIC_UMPC
11052 && gtk_widget_has_focus(compose
->focused_editable
)
11055 /* let text_insert() (called directly or at a later time
11056 * after the gtk_editable_paste_clipboard) know that
11057 * text is to be inserted as a quotation. implemented
11058 * by using a simple refcount... */
11059 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11060 G_OBJECT(compose
->focused_editable
),
11061 "paste_as_quotation"));
11062 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11063 "paste_as_quotation",
11064 GINT_TO_POINTER(paste_as_quotation
+ 1));
11065 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11066 entry_paste_clipboard(compose
, compose
->focused_editable
,
11067 prefs_common
.linewrap_pastes
,
11068 GDK_SELECTION_CLIPBOARD
, NULL
);
11069 prefs_common
.linewrap_quote
= wrap_quote
;
11073 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11075 Compose
*compose
= (Compose
*)data
;
11076 gint prev_autowrap
;
11077 GtkTextBuffer
*buffer
;
11079 if (compose
->focused_editable
11080 #ifndef GENERIC_UMPC
11081 && gtk_widget_has_focus(compose
->focused_editable
)
11084 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11085 GDK_SELECTION_CLIPBOARD
, NULL
);
11090 #ifndef GENERIC_UMPC
11091 gtk_widget_has_focus(compose
->text
) &&
11093 compose
->gtkaspell
&&
11094 compose
->gtkaspell
->check_while_typing
)
11095 gtkaspell_highlight_all(compose
->gtkaspell
);
11099 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11101 Compose
*compose
= (Compose
*)data
;
11102 gint prev_autowrap
;
11103 GtkTextBuffer
*buffer
;
11105 if (compose
->focused_editable
11106 #ifndef GENERIC_UMPC
11107 && gtk_widget_has_focus(compose
->focused_editable
)
11110 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11111 GDK_SELECTION_CLIPBOARD
, NULL
);
11116 #ifndef GENERIC_UMPC
11117 gtk_widget_has_focus(compose
->text
) &&
11119 compose
->gtkaspell
&&
11120 compose
->gtkaspell
->check_while_typing
)
11121 gtkaspell_highlight_all(compose
->gtkaspell
);
11125 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11127 Compose
*compose
= (Compose
*)data
;
11128 if (compose
->focused_editable
11129 #ifndef GENERIC_UMPC
11130 && gtk_widget_has_focus(compose
->focused_editable
)
11133 entry_allsel(compose
->focused_editable
);
11136 static void textview_move_beginning_of_line (GtkTextView
*text
)
11138 GtkTextBuffer
*buffer
;
11142 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11144 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11145 mark
= gtk_text_buffer_get_insert(buffer
);
11146 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11147 gtk_text_iter_set_line_offset(&ins
, 0);
11148 gtk_text_buffer_place_cursor(buffer
, &ins
);
11151 static void textview_move_forward_character (GtkTextView
*text
)
11153 GtkTextBuffer
*buffer
;
11157 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11159 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11160 mark
= gtk_text_buffer_get_insert(buffer
);
11161 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11162 if (gtk_text_iter_forward_cursor_position(&ins
))
11163 gtk_text_buffer_place_cursor(buffer
, &ins
);
11166 static void textview_move_backward_character (GtkTextView
*text
)
11168 GtkTextBuffer
*buffer
;
11172 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11174 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11175 mark
= gtk_text_buffer_get_insert(buffer
);
11176 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11177 if (gtk_text_iter_backward_cursor_position(&ins
))
11178 gtk_text_buffer_place_cursor(buffer
, &ins
);
11181 static void textview_move_forward_word (GtkTextView
*text
)
11183 GtkTextBuffer
*buffer
;
11188 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11190 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11191 mark
= gtk_text_buffer_get_insert(buffer
);
11192 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11193 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11194 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11195 gtk_text_iter_backward_word_start(&ins
);
11196 gtk_text_buffer_place_cursor(buffer
, &ins
);
11200 static void textview_move_backward_word (GtkTextView
*text
)
11202 GtkTextBuffer
*buffer
;
11206 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11208 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11209 mark
= gtk_text_buffer_get_insert(buffer
);
11210 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11211 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11212 gtk_text_buffer_place_cursor(buffer
, &ins
);
11215 static void textview_move_end_of_line (GtkTextView
*text
)
11217 GtkTextBuffer
*buffer
;
11221 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11223 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11224 mark
= gtk_text_buffer_get_insert(buffer
);
11225 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11226 if (gtk_text_iter_forward_to_line_end(&ins
))
11227 gtk_text_buffer_place_cursor(buffer
, &ins
);
11230 static void textview_move_next_line (GtkTextView
*text
)
11232 GtkTextBuffer
*buffer
;
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
);
11242 offset
= gtk_text_iter_get_line_offset(&ins
);
11243 if (gtk_text_iter_forward_line(&ins
)) {
11244 gtk_text_iter_set_line_offset(&ins
, offset
);
11245 gtk_text_buffer_place_cursor(buffer
, &ins
);
11249 static void textview_move_previous_line (GtkTextView
*text
)
11251 GtkTextBuffer
*buffer
;
11256 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11258 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11259 mark
= gtk_text_buffer_get_insert(buffer
);
11260 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11261 offset
= gtk_text_iter_get_line_offset(&ins
);
11262 if (gtk_text_iter_backward_line(&ins
)) {
11263 gtk_text_iter_set_line_offset(&ins
, offset
);
11264 gtk_text_buffer_place_cursor(buffer
, &ins
);
11268 static void textview_delete_forward_character (GtkTextView
*text
)
11270 GtkTextBuffer
*buffer
;
11272 GtkTextIter ins
, end_iter
;
11274 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11276 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11277 mark
= gtk_text_buffer_get_insert(buffer
);
11278 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11280 if (gtk_text_iter_forward_char(&end_iter
)) {
11281 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11285 static void textview_delete_backward_character (GtkTextView
*text
)
11287 GtkTextBuffer
*buffer
;
11289 GtkTextIter ins
, end_iter
;
11291 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11293 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11294 mark
= gtk_text_buffer_get_insert(buffer
);
11295 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11297 if (gtk_text_iter_backward_char(&end_iter
)) {
11298 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11302 static void textview_delete_forward_word (GtkTextView
*text
)
11304 GtkTextBuffer
*buffer
;
11306 GtkTextIter ins
, end_iter
;
11308 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11310 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11311 mark
= gtk_text_buffer_get_insert(buffer
);
11312 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11314 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11315 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11319 static void textview_delete_backward_word (GtkTextView
*text
)
11321 GtkTextBuffer
*buffer
;
11323 GtkTextIter ins
, end_iter
;
11325 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11327 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11328 mark
= gtk_text_buffer_get_insert(buffer
);
11329 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11331 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11332 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11336 static void textview_delete_line (GtkTextView
*text
)
11338 GtkTextBuffer
*buffer
;
11340 GtkTextIter ins
, start_iter
, end_iter
;
11342 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11344 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11345 mark
= gtk_text_buffer_get_insert(buffer
);
11346 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11349 gtk_text_iter_set_line_offset(&start_iter
, 0);
11352 if (gtk_text_iter_ends_line(&end_iter
)){
11353 if (!gtk_text_iter_forward_char(&end_iter
))
11354 gtk_text_iter_backward_char(&start_iter
);
11357 gtk_text_iter_forward_to_line_end(&end_iter
);
11358 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11361 static void textview_delete_to_line_end (GtkTextView
*text
)
11363 GtkTextBuffer
*buffer
;
11365 GtkTextIter ins
, end_iter
;
11367 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11369 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11370 mark
= gtk_text_buffer_get_insert(buffer
);
11371 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11373 if (gtk_text_iter_ends_line(&end_iter
))
11374 gtk_text_iter_forward_char(&end_iter
);
11376 gtk_text_iter_forward_to_line_end(&end_iter
);
11377 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11380 #define DO_ACTION(name, act) { \
11381 if(!strcmp(name, a_name)) { \
11385 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11387 const gchar
*a_name
= gtk_action_get_name(action
);
11388 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11389 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11390 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11391 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11392 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11393 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11394 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11395 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11396 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11397 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11398 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11399 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11400 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11401 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11402 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11405 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11407 Compose
*compose
= (Compose
*)data
;
11408 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11409 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11411 action
= compose_call_advanced_action_from_path(gaction
);
11414 void (*do_action
) (GtkTextView
*text
);
11415 } action_table
[] = {
11416 {textview_move_beginning_of_line
},
11417 {textview_move_forward_character
},
11418 {textview_move_backward_character
},
11419 {textview_move_forward_word
},
11420 {textview_move_backward_word
},
11421 {textview_move_end_of_line
},
11422 {textview_move_next_line
},
11423 {textview_move_previous_line
},
11424 {textview_delete_forward_character
},
11425 {textview_delete_backward_character
},
11426 {textview_delete_forward_word
},
11427 {textview_delete_backward_word
},
11428 {textview_delete_line
},
11429 {textview_delete_to_line_end
}
11432 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11434 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11435 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11436 if (action_table
[action
].do_action
)
11437 action_table
[action
].do_action(text
);
11439 g_warning("Not implemented yet.");
11443 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11445 GtkAllocation allocation
;
11449 if (GTK_IS_EDITABLE(widget
)) {
11450 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11451 gtk_editable_set_position(GTK_EDITABLE(widget
),
11454 if ((parent
= gtk_widget_get_parent(widget
))
11455 && (parent
= gtk_widget_get_parent(parent
))
11456 && (parent
= gtk_widget_get_parent(parent
))) {
11457 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11458 gtk_widget_get_allocation(widget
, &allocation
);
11459 gint y
= allocation
.y
;
11460 gint height
= allocation
.height
;
11461 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11462 (GTK_SCROLLED_WINDOW(parent
));
11464 gfloat value
= gtk_adjustment_get_value(shown
);
11465 gfloat upper
= gtk_adjustment_get_upper(shown
);
11466 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11467 if (y
< (int)value
) {
11468 gtk_adjustment_set_value(shown
, y
- 1);
11470 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11471 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11472 gtk_adjustment_set_value(shown
,
11473 y
+ height
- (int)page_size
- 1);
11475 gtk_adjustment_set_value(shown
,
11476 (int)upper
- (int)page_size
- 1);
11483 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11484 compose
->focused_editable
= widget
;
11486 #ifdef GENERIC_UMPC
11487 if (GTK_IS_TEXT_VIEW(widget
)
11488 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11489 g_object_ref(compose
->notebook
);
11490 g_object_ref(compose
->edit_vbox
);
11491 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11492 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11493 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11494 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11495 g_object_unref(compose
->notebook
);
11496 g_object_unref(compose
->edit_vbox
);
11497 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11498 G_CALLBACK(compose_grab_focus_cb
),
11500 gtk_widget_grab_focus(widget
);
11501 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11502 G_CALLBACK(compose_grab_focus_cb
),
11504 } else if (!GTK_IS_TEXT_VIEW(widget
)
11505 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11506 g_object_ref(compose
->notebook
);
11507 g_object_ref(compose
->edit_vbox
);
11508 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11509 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11510 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11511 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11512 g_object_unref(compose
->notebook
);
11513 g_object_unref(compose
->edit_vbox
);
11514 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11515 G_CALLBACK(compose_grab_focus_cb
),
11517 gtk_widget_grab_focus(widget
);
11518 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11519 G_CALLBACK(compose_grab_focus_cb
),
11525 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11527 compose
->modified
= TRUE
;
11528 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11529 #ifndef GENERIC_UMPC
11530 compose_set_title(compose
);
11534 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11536 Compose
*compose
= (Compose
*)data
;
11537 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11540 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11542 Compose
*compose
= (Compose
*)data
;
11543 compose_wrap_all_full(compose
, TRUE
);
11546 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11548 Compose
*compose
= (Compose
*)data
;
11550 message_search_compose(compose
);
11553 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11556 Compose
*compose
= (Compose
*)data
;
11557 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11558 if (compose
->autowrap
)
11559 compose_wrap_all_full(compose
, TRUE
);
11560 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11563 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11566 Compose
*compose
= (Compose
*)data
;
11567 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11570 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11572 Compose
*compose
= (Compose
*)data
;
11574 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11575 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11578 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11580 Compose
*compose
= (Compose
*)data
;
11582 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11583 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11586 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11588 g_free(compose
->privacy_system
);
11589 g_free(compose
->encdata
);
11591 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11592 compose_update_privacy_system_menu_item(compose
, warn
);
11595 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11597 if (folder_item
!= NULL
) {
11598 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11599 privacy_system_can_sign(compose
->privacy_system
)) {
11600 compose_use_signing(compose
,
11601 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11603 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11604 privacy_system_can_encrypt(compose
->privacy_system
)) {
11605 compose_use_encryption(compose
,
11606 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11611 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11613 Compose
*compose
= (Compose
*)data
;
11615 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11616 gtk_widget_show(compose
->ruler_hbox
);
11617 prefs_common
.show_ruler
= TRUE
;
11619 gtk_widget_hide(compose
->ruler_hbox
);
11620 gtk_widget_queue_resize(compose
->edit_vbox
);
11621 prefs_common
.show_ruler
= FALSE
;
11625 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11626 GdkDragContext
*context
,
11629 GtkSelectionData
*data
,
11632 gpointer user_data
)
11634 Compose
*compose
= (Compose
*)user_data
;
11638 type
= gtk_selection_data_get_data_type(data
);
11639 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11640 && gtk_drag_get_source_widget(context
) !=
11641 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11642 list
= uri_list_extract_filenames(
11643 (const gchar
*)gtk_selection_data_get_data(data
));
11644 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11645 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11646 compose_attach_append
11647 (compose
, (const gchar
*)tmp
->data
,
11648 utf8_filename
, NULL
, NULL
);
11649 g_free(utf8_filename
);
11652 compose_changed_cb(NULL
, compose
);
11653 list_free_strings_full(list
);
11654 } else if (gtk_drag_get_source_widget(context
)
11655 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11656 /* comes from our summaryview */
11657 SummaryView
* summaryview
= NULL
;
11658 GSList
* list
= NULL
, *cur
= NULL
;
11660 if (mainwindow_get_mainwindow())
11661 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11664 list
= summary_get_selected_msg_list(summaryview
);
11666 for (cur
= list
; cur
; cur
= cur
->next
) {
11667 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11668 gchar
*file
= NULL
;
11670 file
= procmsg_get_message_file_full(msginfo
,
11673 compose_attach_append(compose
, (const gchar
*)file
,
11674 (const gchar
*)file
, "message/rfc822", NULL
);
11678 g_slist_free(list
);
11682 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11683 GdkDragContext
*drag_context
,
11685 guint time
, gpointer user_data
)
11687 /* not handling this signal makes compose_insert_drag_received_cb
11692 static gboolean completion_set_focus_to_subject
11693 (GtkWidget
*widget
,
11694 GdkEventKey
*event
,
11697 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11699 /* make backtab move to subject field */
11700 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11701 gtk_widget_grab_focus(compose
->subject_entry
);
11707 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11708 GdkDragContext
*drag_context
,
11711 GtkSelectionData
*data
,
11714 gpointer user_data
)
11716 Compose
*compose
= (Compose
*)user_data
;
11722 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11724 type
= gtk_selection_data_get_data_type(data
);
11725 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11726 AlertValue val
= G_ALERTDEFAULT
;
11727 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11729 list
= uri_list_extract_filenames(ddata
);
11730 num_files
= g_list_length(list
);
11731 if (list
== NULL
&& strstr(ddata
, "://")) {
11732 /* Assume a list of no files, and data has ://, is a remote link */
11733 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11734 gchar
*tmpfile
= get_tmp_file();
11735 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11737 compose_insert_file(compose
, tmpfile
);
11738 claws_unlink(tmpfile
);
11740 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11741 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11744 switch (prefs_common
.compose_dnd_mode
) {
11745 case COMPOSE_DND_ASK
:
11746 msg
= g_strdup_printf(
11748 "Do you want to insert the contents of the file "
11749 "into the message body, or attach it to the email?",
11750 "Do you want to insert the contents of the %d files "
11751 "into the message body, or attach them to the email?",
11754 val
= alertpanel_full(_("Insert or attach?"), msg
,
11755 GTK_STOCK_CANCEL
, _("_Insert"), _("_Attach"),
11757 TRUE
, NULL
, ALERT_QUESTION
);
11760 case COMPOSE_DND_INSERT
:
11761 val
= G_ALERTALTERNATE
;
11763 case COMPOSE_DND_ATTACH
:
11764 val
= G_ALERTOTHER
;
11767 /* unexpected case */
11768 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11771 if (val
& G_ALERTDISABLE
) {
11772 val
&= ~G_ALERTDISABLE
;
11773 /* remember what action to perform by default, only if we don't click Cancel */
11774 if (val
== G_ALERTALTERNATE
)
11775 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11776 else if (val
== G_ALERTOTHER
)
11777 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11780 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11781 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11782 list_free_strings_full(list
);
11784 } else if (val
== G_ALERTOTHER
) {
11785 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11786 list_free_strings_full(list
);
11790 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11791 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11793 list_free_strings_full(list
);
11794 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11799 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11800 GdkDragContext
*drag_context
,
11803 GtkSelectionData
*data
,
11806 gpointer user_data
)
11808 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11809 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11811 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11814 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11815 gchar
*decoded
=g_new(gchar
, strlen(email
));
11818 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11819 gtk_editable_delete_text(entry
, 0, -1);
11820 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11821 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11825 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11828 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11830 Compose
*compose
= (Compose
*)data
;
11832 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11833 compose
->return_receipt
= TRUE
;
11835 compose
->return_receipt
= FALSE
;
11838 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11840 Compose
*compose
= (Compose
*)data
;
11842 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11843 compose
->remove_references
= TRUE
;
11845 compose
->remove_references
= FALSE
;
11848 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11849 ComposeHeaderEntry
*headerentry
)
11851 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11852 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11853 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11857 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11858 GdkEventKey
*event
,
11859 ComposeHeaderEntry
*headerentry
)
11861 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11862 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11863 !(event
->state
& GDK_MODIFIER_MASK
) &&
11864 (event
->keyval
== GDK_KEY_BackSpace
) &&
11865 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11866 gtk_container_remove
11867 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11868 headerentry
->combo
);
11869 gtk_container_remove
11870 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11871 headerentry
->entry
);
11872 headerentry
->compose
->header_list
=
11873 g_slist_remove(headerentry
->compose
->header_list
,
11875 g_free(headerentry
);
11876 } else if (event
->keyval
== GDK_KEY_Tab
) {
11877 if (headerentry
->compose
->header_last
== headerentry
) {
11878 /* Override default next focus, and give it to subject_entry
11879 * instead of notebook tabs
11881 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11882 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11889 static gboolean
scroll_postpone(gpointer data
)
11891 Compose
*compose
= (Compose
*)data
;
11893 if (compose
->batch
)
11896 GTK_EVENTS_FLUSH();
11897 compose_show_first_last_header(compose
, FALSE
);
11901 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11902 ComposeHeaderEntry
*headerentry
)
11904 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11905 compose_create_header_entry(headerentry
->compose
);
11906 g_signal_handlers_disconnect_matched
11907 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11908 0, 0, NULL
, NULL
, headerentry
);
11910 if (!headerentry
->compose
->batch
)
11911 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11915 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11917 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11918 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11922 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11924 GtkAdjustment
*vadj
;
11926 cm_return_if_fail(compose
);
11931 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11932 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11933 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11934 gtk_widget_get_parent(compose
->header_table
)));
11935 gtk_adjustment_set_value(vadj
, (show_first
?
11936 gtk_adjustment_get_lower(vadj
) :
11937 (gtk_adjustment_get_upper(vadj
) -
11938 gtk_adjustment_get_page_size(vadj
))));
11939 gtk_adjustment_changed(vadj
);
11942 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11943 const gchar
*text
, gint len
, Compose
*compose
)
11945 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11946 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11949 cm_return_if_fail(text
!= NULL
);
11951 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11952 G_CALLBACK(text_inserted
),
11954 if (paste_as_quotation
) {
11956 const gchar
*qmark
;
11958 GtkTextIter start_iter
;
11961 len
= strlen(text
);
11963 new_text
= g_strndup(text
, len
);
11965 qmark
= compose_quote_char_from_context(compose
);
11967 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11968 gtk_text_buffer_place_cursor(buffer
, iter
);
11970 pos
= gtk_text_iter_get_offset(iter
);
11972 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11973 _("Quote format error at line %d."));
11974 quote_fmt_reset_vartable();
11976 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11977 GINT_TO_POINTER(paste_as_quotation
- 1));
11979 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11980 gtk_text_buffer_place_cursor(buffer
, iter
);
11981 gtk_text_buffer_delete_mark(buffer
, mark
);
11983 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11984 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11985 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11986 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11987 gtk_text_buffer_delete_mark(buffer
, mark
);
11989 if (strcmp(text
, "\n") || compose
->automatic_break
11990 || gtk_text_iter_starts_line(iter
)) {
11991 GtkTextIter before_ins
;
11992 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11993 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11994 before_ins
= *iter
;
11995 gtk_text_iter_backward_chars(&before_ins
, len
);
11996 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11999 /* check if the preceding is just whitespace or quote */
12000 GtkTextIter start_line
;
12001 gchar
*tmp
= NULL
, *quote
= NULL
;
12002 gint quote_len
= 0, is_normal
= 0;
12003 start_line
= *iter
;
12004 gtk_text_iter_set_line_offset(&start_line
, 0);
12005 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
12008 if (*tmp
== '\0') {
12011 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
12019 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
12021 gtk_text_buffer_insert_with_tags_by_name(buffer
,
12022 iter
, text
, len
, "no_join", NULL
);
12027 if (!paste_as_quotation
) {
12028 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12029 compose_beautify_paragraph(compose
, iter
, FALSE
);
12030 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12031 gtk_text_buffer_delete_mark(buffer
, mark
);
12034 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12035 G_CALLBACK(text_inserted
),
12037 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12039 if (compose_can_autosave(compose
) &&
12040 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12041 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12042 compose
->draft_timeout_tag
= g_timeout_add
12043 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12047 static void compose_check_all(GtkAction
*action
, gpointer data
)
12049 Compose
*compose
= (Compose
*)data
;
12050 if (!compose
->gtkaspell
)
12053 if (gtk_widget_has_focus(compose
->subject_entry
))
12054 claws_spell_entry_check_all(
12055 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12057 gtkaspell_check_all(compose
->gtkaspell
);
12060 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12062 Compose
*compose
= (Compose
*)data
;
12063 if (compose
->gtkaspell
) {
12064 claws_spell_entry_recheck_all(
12065 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12066 gtkaspell_highlight_all(compose
->gtkaspell
);
12070 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12072 Compose
*compose
= (Compose
*)data
;
12073 if (!compose
->gtkaspell
) {
12074 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12078 if (gtk_widget_has_focus(compose
->subject_entry
))
12079 claws_spell_entry_check_backwards(
12080 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12082 gtkaspell_check_backwards(compose
->gtkaspell
);
12085 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12087 Compose
*compose
= (Compose
*)data
;
12088 if (!compose
->gtkaspell
) {
12089 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12093 if (gtk_widget_has_focus(compose
->subject_entry
))
12094 claws_spell_entry_check_forwards_go(
12095 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12097 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12102 *\brief Guess originating forward account from MsgInfo and several
12103 * "common preference" settings. Return NULL if no guess.
12105 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12107 PrefsAccount
*account
= NULL
;
12109 cm_return_val_if_fail(msginfo
, NULL
);
12110 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12111 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12113 if (msginfo
->folder
->prefs
->enable_default_account
)
12114 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12116 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12118 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12119 extract_address(to
);
12120 account
= account_find_from_address(to
, FALSE
);
12123 if (!account
&& prefs_common
.forward_account_autosel
) {
12125 if (!procheader_get_header_from_msginfo
12126 (msginfo
, &cc
, "Cc:")) {
12127 gchar
*buf
= cc
+ strlen("Cc:");
12128 extract_address(buf
);
12129 account
= account_find_from_address(buf
, FALSE
);
12134 if (!account
&& prefs_common
.forward_account_autosel
) {
12135 gchar
*deliveredto
= NULL
;
12136 if (!procheader_get_header_from_msginfo
12137 (msginfo
, &deliveredto
, "Delivered-To:")) {
12138 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12139 extract_address(buf
);
12140 account
= account_find_from_address(buf
, FALSE
);
12141 g_free(deliveredto
);
12146 account
= msginfo
->folder
->folder
->account
;
12151 gboolean
compose_close(Compose
*compose
)
12155 cm_return_val_if_fail(compose
, FALSE
);
12157 if (!g_mutex_trylock(compose
->mutex
)) {
12158 /* we have to wait for the (possibly deferred by auto-save)
12159 * drafting to be done, before destroying the compose under
12161 debug_print("waiting for drafting to finish...\n");
12162 compose_allow_user_actions(compose
, FALSE
);
12163 if (compose
->close_timeout_tag
== 0) {
12164 compose
->close_timeout_tag
=
12165 g_timeout_add (500, (GSourceFunc
) compose_close
,
12171 if (compose
->draft_timeout_tag
>= 0) {
12172 g_source_remove(compose
->draft_timeout_tag
);
12173 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12176 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
12177 if (!compose
->batch
) {
12178 prefs_common
.compose_x
= x
;
12179 prefs_common
.compose_y
= y
;
12181 g_mutex_unlock(compose
->mutex
);
12182 compose_destroy(compose
);
12187 * Add entry field for each address in list.
12188 * \param compose E-Mail composition object.
12189 * \param listAddress List of (formatted) E-Mail addresses.
12191 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12194 node
= listAddress
;
12196 addr
= ( gchar
* ) node
->data
;
12197 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12198 node
= g_list_next( node
);
12202 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12203 guint action
, gboolean opening_multiple
)
12205 gchar
*body
= NULL
;
12206 GSList
*new_msglist
= NULL
;
12207 MsgInfo
*tmp_msginfo
= NULL
;
12208 gboolean originally_enc
= FALSE
;
12209 gboolean originally_sig
= FALSE
;
12210 Compose
*compose
= NULL
;
12211 gchar
*s_system
= NULL
;
12213 cm_return_if_fail(msginfo_list
!= NULL
);
12215 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12216 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12217 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12219 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12220 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12221 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12222 orig_msginfo
, mimeinfo
);
12223 if (tmp_msginfo
!= NULL
) {
12224 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12226 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12227 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12228 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12230 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12231 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12232 if (orig_msginfo
->tags
) {
12233 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12234 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12240 if (!opening_multiple
&& msgview
!= NULL
)
12241 body
= messageview_get_selection(msgview
);
12244 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12245 procmsg_msginfo_free(&tmp_msginfo
);
12246 g_slist_free(new_msglist
);
12248 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12250 if (compose
&& originally_enc
) {
12251 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12254 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12255 compose_force_signing(compose
, compose
->account
, s_system
);
12259 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12262 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12265 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12266 && msginfo_list
!= NULL
12267 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12268 GSList
*cur
= msginfo_list
;
12269 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12270 "messages. Opening the windows "
12271 "could take some time. Do you "
12272 "want to continue?"),
12273 g_slist_length(msginfo_list
));
12274 if (g_slist_length(msginfo_list
) > 9
12275 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_YES
, NULL
,
12276 ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12281 /* We'll open multiple compose windows */
12282 /* let the WM place the next windows */
12283 compose_force_window_origin
= FALSE
;
12284 for (; cur
; cur
= cur
->next
) {
12286 tmplist
.data
= cur
->data
;
12287 tmplist
.next
= NULL
;
12288 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12290 compose_force_window_origin
= TRUE
;
12292 /* forwarding multiple mails as attachments is done via a
12293 * single compose window */
12294 if (msginfo_list
!= NULL
) {
12295 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12296 } else if (msgview
!= NULL
) {
12298 tmplist
.data
= msgview
->msginfo
;
12299 tmplist
.next
= NULL
;
12300 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12302 debug_print("Nothing to reply to\n");
12307 void compose_check_for_email_account(Compose
*compose
)
12309 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12315 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12316 ac
= account_get_cur_account();
12317 if (ac
->protocol
== A_NNTP
) {
12318 list
= account_get_list();
12320 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12321 curr
= (PrefsAccount
*) list
->data
;
12322 if (curr
->protocol
!= A_NNTP
) {
12328 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12333 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12334 const gchar
*address
)
12336 GSList
*msginfo_list
= NULL
;
12337 gchar
*body
= messageview_get_selection(msgview
);
12340 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12342 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12343 compose_check_for_email_account(compose
);
12344 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12345 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12346 compose_reply_set_subject(compose
, msginfo
);
12349 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12352 void compose_set_position(Compose
*compose
, gint pos
)
12354 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12356 gtkut_text_view_set_position(text
, pos
);
12359 gboolean
compose_search_string(Compose
*compose
,
12360 const gchar
*str
, gboolean case_sens
)
12362 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12364 return gtkut_text_view_search_string(text
, str
, case_sens
);
12367 gboolean
compose_search_string_backward(Compose
*compose
,
12368 const gchar
*str
, gboolean case_sens
)
12370 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12372 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12375 /* allocate a msginfo structure and populate its data from a compose data structure */
12376 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12378 MsgInfo
*newmsginfo
;
12380 gchar date
[RFC822_DATE_BUFFSIZE
];
12382 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12384 newmsginfo
= procmsg_msginfo_new();
12387 get_rfc822_date(date
, sizeof(date
));
12388 newmsginfo
->date
= g_strdup(date
);
12391 if (compose
->from_name
) {
12392 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12393 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12397 if (compose
->subject_entry
)
12398 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12400 /* to, cc, reply-to, newsgroups */
12401 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12402 gchar
*header
= gtk_editable_get_chars(
12404 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12405 gchar
*entry
= gtk_editable_get_chars(
12406 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12408 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12409 if ( newmsginfo
->to
== NULL
) {
12410 newmsginfo
->to
= g_strdup(entry
);
12411 } else if (entry
&& *entry
) {
12412 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12413 g_free(newmsginfo
->to
);
12414 newmsginfo
->to
= tmp
;
12417 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12418 if ( newmsginfo
->cc
== NULL
) {
12419 newmsginfo
->cc
= g_strdup(entry
);
12420 } else if (entry
&& *entry
) {
12421 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12422 g_free(newmsginfo
->cc
);
12423 newmsginfo
->cc
= tmp
;
12426 if ( strcasecmp(header
,
12427 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12428 if ( newmsginfo
->newsgroups
== NULL
) {
12429 newmsginfo
->newsgroups
= g_strdup(entry
);
12430 } else if (entry
&& *entry
) {
12431 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12432 g_free(newmsginfo
->newsgroups
);
12433 newmsginfo
->newsgroups
= tmp
;
12441 /* other data is unset */
12447 /* update compose's dictionaries from folder dict settings */
12448 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12449 FolderItem
*folder_item
)
12451 cm_return_if_fail(compose
!= NULL
);
12453 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12454 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12456 if (prefs
->enable_default_dictionary
)
12457 gtkaspell_change_dict(compose
->gtkaspell
,
12458 prefs
->default_dictionary
, FALSE
);
12459 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12460 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12461 prefs
->default_alt_dictionary
);
12462 if (prefs
->enable_default_dictionary
12463 || prefs
->enable_default_alt_dictionary
)
12464 compose_spell_menu_changed(compose
);
12469 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12471 Compose
*compose
= (Compose
*)data
;
12473 cm_return_if_fail(compose
!= NULL
);
12475 gtk_widget_grab_focus(compose
->text
);
12478 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12480 gtk_combo_box_popup(GTK_COMBO_BOX(data
));