2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkitemfactory.h>
32 #include <gtk/gtkcheckmenuitem.h>
33 #include <gtk/gtkoptionmenu.h>
34 #include <gtk/gtkwidget.h>
35 #include <gtk/gtkclist.h>
36 #include <gtk/gtkctree.h>
37 #include <gtk/gtkvpaned.h>
38 #include <gtk/gtkentry.h>
39 #include <gtk/gtkeditable.h>
40 #include <gtk/gtkwindow.h>
41 #include <gtk/gtksignal.h>
42 #include <gtk/gtkvbox.h>
43 #include <gtk/gtkcontainer.h>
44 #include <gtk/gtkhandlebox.h>
45 #include <gtk/gtktoolbar.h>
46 #include <gtk/gtktable.h>
47 #include <gtk/gtkhbox.h>
48 #include <gtk/gtklabel.h>
49 #include <gtk/gtkscrolledwindow.h>
50 #include <gtk/gtkthemes.h>
51 #include <gtk/gtkdnd.h>
58 /* #include <sys/utsname.h> */
64 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
74 #include "mainwindow.h"
76 #include "addressbook.h"
77 #include "folderview.h"
82 #include "customheader.h"
83 #include "prefs_common.h"
84 #include "prefs_account.h"
87 #include "procheader.h"
89 #include "statusbar.h"
96 #include "alertpanel.h"
97 #include "manage_window.h"
98 #include "gtkshruler.h"
100 #include "addr_compl.h"
101 #include "template_select.h"
104 # include "rfc2015.h"
107 #include "quote_fmt.h"
116 #define N_ATTACH_COLS 3
118 #define B64_LINE_SIZE 57
119 #define B64_BUFFSIZE 77
121 #define MAX_REFERENCES_LEN 999
123 static GdkColor quote_color
= {0, 0, 0, 0xbfff};
125 static GList
*compose_list
= NULL
;
127 static Compose
*compose_create (PrefsAccount
*account
);
128 static void compose_toolbar_create (Compose
*compose
,
129 GtkWidget
*container
);
130 static GtkWidget
*compose_account_option_menu_create
132 static void compose_destroy (Compose
*compose
);
134 static gint
compose_parse_header (Compose
*compose
,
136 static gchar
*compose_parse_references (const gchar
*ref
,
138 static void compose_quote_file (Compose
*compose
,
141 static gchar
*compose_quote_parse_fmt (Compose
*compose
,
144 static void compose_reply_set_entry (Compose
*compose
,
149 followup_and_reply_to
);
150 static void compose_reedit_set_entry (Compose
*compose
,
152 static void compose_insert_sig (Compose
*compose
);
153 static void compose_insert_file (Compose
*compose
,
155 static void compose_attach_append (Compose
*compose
,
157 ContentType cnttype
);
158 static void compose_attach_append_with_type(Compose
*compose
,
161 ContentType cnttype
);
162 static void compose_wrap_line (Compose
*compose
);
163 static void compose_set_title (Compose
*compose
);
165 static PrefsAccount
*compose_current_mail_account(void);
166 /* static gint compose_send (Compose *compose); */
167 static gint
compose_write_to_file (Compose
*compose
,
170 static gint
compose_write_body_to_file (Compose
*compose
,
172 static gint
compose_save_to_outbox (Compose
*compose
,
174 static gint
compose_queue (Compose
*compose
,
176 static void compose_write_attach (Compose
*compose
,
178 static gint
compose_write_headers (Compose
*compose
,
180 const gchar
*charset
,
181 EncodingType encoding
,
184 static void compose_convert_header (gchar
*dest
,
188 static void compose_generate_msgid (Compose
*compose
,
192 static void compose_attach_info_free (AttachInfo
*ainfo
);
193 static void compose_attach_remove_selected (Compose
*compose
);
195 static void compose_attach_property (Compose
*compose
);
196 static void compose_attach_property_create (gboolean
*cancelled
);
197 static void attach_property_ok (GtkWidget
*widget
,
198 gboolean
*cancelled
);
199 static void attach_property_cancel (GtkWidget
*widget
,
200 gboolean
*cancelled
);
201 static gint
attach_property_delete_event (GtkWidget
*widget
,
203 gboolean
*cancelled
);
204 static void attach_property_key_pressed (GtkWidget
*widget
,
206 gboolean
*cancelled
);
208 static void compose_exec_ext_editor (Compose
*compose
);
209 static gint
compose_exec_ext_editor_real (const gchar
*file
);
210 static gboolean
compose_ext_editor_kill (Compose
*compose
);
211 static void compose_input_cb (gpointer data
,
213 GdkInputCondition condition
);
214 static void compose_set_ext_editor_sensitive (Compose
*compose
,
217 static gint
calc_cursor_xpos (GtkSText
*text
,
221 /* callback functions */
223 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
224 GtkAllocation
*allocation
,
225 GtkSHRuler
*shruler
);
227 static void toolbar_send_cb (GtkWidget
*widget
,
229 static void toolbar_send_later_cb (GtkWidget
*widget
,
231 static void toolbar_draft_cb (GtkWidget
*widget
,
233 static void toolbar_insert_cb (GtkWidget
*widget
,
235 static void toolbar_attach_cb (GtkWidget
*widget
,
237 static void toolbar_sig_cb (GtkWidget
*widget
,
239 static void toolbar_ext_editor_cb (GtkWidget
*widget
,
241 static void toolbar_linewrap_cb (GtkWidget
*widget
,
243 static void toolbar_address_cb (GtkWidget
*widget
,
245 static void template_select_cb (gpointer daat
,
250 static void select_account(Compose
* compose
, PrefsAccount
* ac
);
251 static void account_activated (GtkMenuItem
*menuitem
,
254 static void attach_selected (GtkCList
*clist
,
259 static void attach_button_pressed (GtkWidget
*widget
,
260 GdkEventButton
*event
,
262 static void attach_key_pressed (GtkWidget
*widget
,
266 static void compose_send_cb (gpointer data
,
269 static void compose_send_later_cb (gpointer data
,
273 static void compose_draft_cb (gpointer data
,
277 static void compose_attach_cb (gpointer data
,
280 static void compose_insert_file_cb (gpointer data
,
284 static void compose_close_cb (gpointer data
,
287 static void compose_address_cb (gpointer data
,
291 static void compose_ext_editor_cb (gpointer data
,
295 static gint
compose_delete_cb (GtkWidget
*widget
,
298 static void compose_destroy_cb (GtkWidget
*widget
,
301 static void compose_cut_cb (Compose
*compose
);
302 static void compose_copy_cb (Compose
*compose
);
303 static void compose_paste_cb (Compose
*compose
);
304 static void compose_allsel_cb (Compose
*compose
);
306 static void compose_grab_focus_cb (GtkWidget
*widget
,
309 static void compose_changed_cb (GtkEditable
*editable
,
311 static void compose_button_press_cb (GtkWidget
*widget
,
312 GdkEventButton
*event
,
315 static void compose_key_press_cb (GtkWidget
*widget
,
320 static void compose_toggle_to_cb (gpointer data
,
323 static void compose_toggle_cc_cb (gpointer data
,
326 static void compose_toggle_bcc_cb (gpointer data
,
329 static void compose_toggle_replyto_cb (gpointer data
,
332 static void compose_toggle_followupto_cb(gpointer data
,
335 static void compose_toggle_attach_cb (gpointer data
,
338 static void compose_toggle_ruler_cb (gpointer data
,
342 static void compose_toggle_sign_cb (gpointer data
,
345 static void compose_toggle_encrypt_cb (gpointer data
,
349 static void compose_toggle_return_receipt_cb(gpointer data
, guint action
,
352 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
353 GdkDragContext
*drag_context
,
356 GtkSelectionData
*data
,
360 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
361 GdkDragContext
*drag_context
,
364 GtkSelectionData
*data
,
369 static void to_activated (GtkWidget
*widget
,
371 static void newsgroups_activated (GtkWidget
*widget
,
373 static void subject_activated (GtkWidget
*widget
,
375 static void cc_activated (GtkWidget
*widget
,
377 static void bcc_activated (GtkWidget
*widget
,
379 static void replyto_activated (GtkWidget
*widget
,
381 static void followupto_activated (GtkWidget
*widget
,
383 static void compose_attach_parts(Compose
* compose
,
386 static gchar
*compose_quote_fmt (Compose
*compose
,
389 const gchar
* qmark
);
391 static void compose_generic_reply(MsgInfo
*msginfo
, gboolean quote
,
393 gboolean ignore_replyto
,
394 gboolean followup_and_reply_to
);
396 Compose
* compose_generic_new (PrefsAccount
*account
,
400 static GtkItemFactoryEntry compose_popup_entries
[] =
402 {N_("/_Add..."), NULL
, compose_attach_cb
, 0, NULL
},
403 {N_("/_Remove"), NULL
, compose_attach_remove_selected
, 0, NULL
},
404 {N_("/---"), NULL
, NULL
, 0, "<Separator>"},
405 {N_("/_Property..."), NULL
, compose_attach_property
, 0, NULL
}
408 static GtkItemFactoryEntry compose_entries
[] =
410 {N_("/_File"), NULL
, NULL
, 0, "<Branch>"},
411 {N_("/_File/_Attach file"), "<control>M", compose_attach_cb
, 0, NULL
},
412 {N_("/_File/_Insert file"), "<control>I", compose_insert_file_cb
, 0, NULL
},
413 {N_("/_File/Insert si_gnature"), "<control>G", compose_insert_sig
, 0, NULL
},
414 {N_("/_File/---"), NULL
, NULL
, 0, "<Separator>"},
415 {N_("/_File/_Close"), "<alt>W", compose_close_cb
, 0, NULL
},
417 {N_("/_Edit"), NULL
, NULL
, 0, "<Branch>"},
418 {N_("/_Edit/_Undo"), "<control>Z", NULL
, 0, NULL
},
419 {N_("/_Edit/_Redo"), "<control>Y", NULL
, 0, NULL
},
420 {N_("/_Edit/---"), NULL
, NULL
, 0, "<Separator>"},
421 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb
, 0, NULL
},
422 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb
, 0, NULL
},
423 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb
, 0, NULL
},
424 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb
, 0, NULL
},
425 {N_("/_Edit/---"), NULL
, NULL
, 0, "<Separator>"},
426 {N_("/_Edit/Wrap long _lines"), "<alt>L", compose_wrap_line
, 0, NULL
},
427 {N_("/_Edit/Edit with e_xternal editor"), "<alt>X",
428 compose_ext_editor_cb
, 0, NULL
},
430 {N_("/_Message"), NULL
, NULL
, 0, "<Branch>"},
431 {N_("/_Message/_Send"), "<shift><control>S",
432 compose_send_cb
, 0, NULL
},
433 {N_("/_Message/Send _later"), "<shift><alt>S",
434 compose_send_later_cb
, 0, NULL
},
435 {N_("/_Message/Save to _draft folder"),
436 "<alt>D", compose_draft_cb
, 0, NULL
},
437 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
438 {N_("/_Message/_To"), NULL
, compose_toggle_to_cb
, 0, "<ToggleItem>"},
439 {N_("/_Message/_Cc"), NULL
, compose_toggle_cc_cb
, 0, "<ToggleItem>"},
440 {N_("/_Message/_Bcc"), NULL
, compose_toggle_bcc_cb
, 0, "<ToggleItem>"},
441 {N_("/_Message/_Reply to"), NULL
, compose_toggle_replyto_cb
, 0, "<ToggleItem>"},
442 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
443 {N_("/_Message/_Followup to"), NULL
, compose_toggle_followupto_cb
, 0, "<ToggleItem>"},
444 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
445 {N_("/_Message/_Attach"), NULL
, compose_toggle_attach_cb
, 0, "<ToggleItem>"},
447 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
448 {N_("/_Message/Si_gn"), NULL
, compose_toggle_sign_cb
, 0, "<ToggleItem>"},
449 {N_("/_Message/_Encrypt"), NULL
, compose_toggle_encrypt_cb
, 0, "<ToggleItem>"},
450 #endif /* USE_GPGME */
451 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
452 {N_("/_Message/_Request Return Receipt"), NULL
, compose_toggle_return_receipt_cb
, 0, "<ToggleItem>"},
453 {N_("/_Tool"), NULL
, NULL
, 0, "<Branch>"},
454 {N_("/_Tool/Show _ruler"), NULL
, compose_toggle_ruler_cb
, 0, "<ToggleItem>"},
455 {N_("/_Tool/_Address book"), "<alt>A", compose_address_cb
, 0, NULL
},
456 {N_("/_Tool/_Templates ..."), NULL
, template_select_cb
, 0, NULL
},
457 {N_("/_Help"), NULL
, NULL
, 0, "<LastBranch>"},
458 {N_("/_Help/_About"), NULL
, about_show
, 0, NULL
}
461 static GtkTargetEntry compose_mime_types
[] =
463 {"text/uri-list", 0, 0}
466 Compose
* compose_new(PrefsAccount
*account
)
468 return compose_generic_new(account
, NULL
, NULL
);
471 Compose
* compose_new_with_recipient(PrefsAccount
*account
, const gchar
*to
)
473 return compose_generic_new(account
, to
, NULL
);
476 Compose
* compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
)
478 return compose_generic_new(account
, NULL
, item
);
481 Compose
* compose_generic_new(PrefsAccount
*account
, const gchar
*to
, FolderItem
*item
)
485 if (!account
) account
= cur_account
;
486 g_return_val_if_fail(account
!= NULL
, NULL
);
488 compose
= compose_create(account
);
489 compose
->mode
= COMPOSE_NEW
;
490 compose
->replyinfo
= NULL
;
492 if (prefs_common
.auto_sig
)
493 compose_insert_sig(compose
);
494 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
495 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
497 if (account
->protocol
!= A_NNTP
) {
499 compose_entry_append(compose
, to
, COMPOSE_TO
);
500 gtk_widget_grab_focus(compose
->subject_entry
);
502 if(item
&& item
->prefs
->enable_default_to
) {
503 compose_entry_append(compose
, item
->prefs
->default_to
, COMPOSE_TO
);
505 gtk_widget_grab_focus(compose
->to_entry
);
508 if(item
&& item
->prefs
->request_return_receipt
) {
509 GtkItemFactory
*ifactory
;
511 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
512 menu_set_toggle(ifactory
, "/Message/Request Return Receipt", TRUE
);
516 compose_entry_append(compose
, to
, COMPOSE_NEWSGROUPS
);
517 gtk_widget_grab_focus(compose
->subject_entry
);
519 gtk_widget_grab_focus(compose
->newsgroups_entry
);
525 #define CHANGE_FLAGS(msginfo) \
527 if (msginfo->folder->folder->change_flags != NULL) \
528 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
534 Compose * compose_new_followup_and_replyto(PrefsAccount *account,
535 const gchar *followupto, gchar * to)
539 if (!account) account = cur_account;
540 g_return_val_if_fail(account != NULL, NULL);
541 g_return_val_if_fail(account->protocol != A_NNTP, NULL);
543 compose = compose_create(account);
544 compose->mode = COMPOSE_NEW;
546 if (prefs_common.auto_sig)
547 compose_insert_sig(compose);
548 gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
549 gtk_stext_set_point(GTK_STEXT(compose->text), 0);
551 compose_entry_append(compose, to, COMPOSE_TO);
552 compose_entry_append(compose, followupto, COMPOSE_NEWSGROUPS);
553 gtk_widget_grab_focus(compose->subject_entry);
559 void compose_reply(MsgInfo
*msginfo
, gboolean quote
, gboolean to_all
,
560 gboolean ignore_replyto
)
562 compose_generic_reply(msginfo
, quote
, to_all
, ignore_replyto
, FALSE
);
565 void compose_followup_and_reply_to(MsgInfo
*msginfo
, gboolean quote
,
567 gboolean ignore_replyto
)
569 compose_generic_reply(msginfo
, quote
, to_all
, ignore_replyto
, TRUE
);
572 static void compose_generic_reply(MsgInfo
*msginfo
, gboolean quote
,
574 gboolean ignore_replyto
,
575 gboolean followup_and_reply_to
)
578 PrefsAccount
*account
;
579 PrefsAccount
*reply_account
;
582 g_return_if_fail(msginfo
!= NULL
);
583 g_return_if_fail(msginfo
->folder
!= NULL
);
585 account
= msginfo
->folder
->folder
->account
;
586 if (!account
&& msginfo
->to
&& prefs_common
.reply_account_autosel
) {
588 Xstrdup_a(to
, msginfo
->to
, return);
590 account
= account_find_from_address(to
);
592 if (!account
) account
= cur_account
;
593 g_return_if_fail(account
!= NULL
);
595 if (ignore_replyto
&& account
->protocol
== A_NNTP
&&
596 !followup_and_reply_to
) {
598 account_find_from_address(account
->address
);
600 reply_account
= compose_current_mail_account();
604 reply_account
= account
;
605 MSG_UNSET_PERM_FLAGS(msginfo
->flags
, MSG_FORWARDED
);
606 MSG_SET_PERM_FLAGS(msginfo
->flags
, MSG_REPLIED
);
608 CHANGE_FLAGS(msginfo
);
610 compose
= compose_create(reply_account
);
611 compose
->mode
= COMPOSE_REPLY
;
612 compose
->replyinfo
= msginfo
;
615 if (followup_and_reply_to
) {
616 gtk_widget_show(compose
->to_hbox
);
617 gtk_widget_show(compose
->to_entry
);
618 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 1, 4);
619 compose
->use_to
= TRUE
;
622 if (compose_parse_header(compose
, msginfo
) < 0) return;
623 compose_reply_set_entry(compose
, msginfo
, to_all
, ignore_replyto
,
624 followup_and_reply_to
);
626 text
= GTK_STEXT(compose
->text
);
627 gtk_stext_freeze(text
);
633 if ((fp
= procmime_get_text_part(msginfo
)) == NULL
)
634 g_warning(_("Can't get text part\n"));
638 if (prefs_common
.quotemark
&& *prefs_common
.quotemark
)
639 qmark
= prefs_common
.quotemark
;
643 quote_str
= compose_quote_fmt(compose
, msginfo
,
644 prefs_common
.quotefmt
,
648 quote_str = compose_quote_parse_fmt
649 (compose, msginfo, prefs_common.quotefmt);
652 if (quote_str
!= NULL
)
653 gtk_stext_insert(text
, NULL
, NULL
, NULL
,
655 /* g_free(quote_str); */
656 /* compose_quote_file(compose, msginfo, fp); */
661 if (prefs_common
.auto_sig
)
662 compose_insert_sig(compose
);
663 gtk_editable_set_position(GTK_EDITABLE(text
), 0);
664 gtk_stext_set_point(text
, 0);
666 gtk_stext_thaw(text
);
667 gtk_widget_grab_focus(compose
->text
);
671 static gchar
*procmime_get_file_name(MimeInfo
*mimeinfo
)
676 g_return_val_if_fail(mimeinfo
!= NULL
, NULL
);
678 base
= mimeinfo
->filename
? mimeinfo
->filename
679 : mimeinfo
->name
? mimeinfo
->name
: NULL
;
681 if (MIME_TEXT_HTML
== mimeinfo
->mime_type
&& base
== NULL
){
682 filename
= g_strdup_printf("%s%smimetmp%i.html",
689 base
= base
? base
: "";
690 base
= g_basename(base
);
692 filename
= g_strdup_printf("%s%smimetmp%i",
700 filename
= g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S
,
706 static gchar
* mime_extract_file(gchar
* source
, MimeInfo
*partinfo
)
710 if (!partinfo
) return;
712 filename
= procmime_get_file_name(partinfo
);
714 if (procmime_get_part(filename
, source
, partinfo
) < 0)
716 (_("Can't get the part of multipart message."));
721 static void compose_attach_parts(Compose
* compose
,
731 gint boundary_len
= 0;
733 glong fpos
, prev_fpos
;
738 g_return_if_fail(msginfo
!= NULL
);
742 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return;
743 mimeinfo
= procmime_scan_mime_header(fp
);
744 if (!mimeinfo
) break;
746 if (!MSG_IS_ENCRYPTED(msginfo
->flags
) &&
747 rfc2015_is_encrypted(mimeinfo
)) {
748 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_ENCRYPTED
);
750 if (MSG_IS_ENCRYPTED(msginfo
->flags
) &&
751 !msginfo
->plaintext_file
&&
752 !msginfo
->decryption_failed
) {
753 rfc2015_decrypt_message(msginfo
, mimeinfo
, fp
);
754 if (msginfo
->plaintext_file
&&
755 !msginfo
->decryption_failed
) {
763 #else /* !USE_GPGME */
764 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return;
765 mimeinfo
= procmime_scan_mime_header(fp
);
766 #endif /* USE_GPGME */
769 if (!mimeinfo
) return;
770 if (mimeinfo
->mime_type
== MIME_TEXT
)
773 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return;
775 g_return_if_fail(mimeinfo
!= NULL
);
776 g_return_if_fail(mimeinfo
->mime_type
!= MIME_TEXT
);
778 if (mimeinfo
->mime_type
== MIME_MULTIPART
) {
779 g_return_if_fail(mimeinfo
->boundary
!= NULL
);
780 g_return_if_fail(mimeinfo
->sub
== NULL
);
782 g_return_if_fail(fp
!= NULL
);
784 boundary
= mimeinfo
->boundary
;
787 boundary_len
= strlen(boundary
);
789 /* look for first boundary */
790 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
)
791 if (IS_BOUNDARY(buf
, boundary
, boundary_len
)) break;
798 if ((fpos
= ftell(fp
)) < 0) {
804 for (npart
= 0;; npart
++) {
806 gboolean eom
= FALSE
;
810 partinfo
= procmime_scan_mime_header(fp
);
811 if (!partinfo
) break;
814 procmime_mimeinfo_insert(mimeinfo
, partinfo
);
816 procmime_mimeinfo_free(partinfo
);
818 /* look for next boundary */
820 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
821 if (IS_BOUNDARY(buf
, boundary
, boundary_len
)) {
822 if (buf
[2 + boundary_len
] == '-' &&
823 buf
[2 + boundary_len
+ 1] == '-')
829 eom
= TRUE
; /* broken MIME message */
832 partinfo
->size
= fpos
- prev_fpos
- strlen(buf
);
837 source
= procmsg_get_message_file_path(msginfo
);
839 g_return_if_fail(mimeinfo
!= NULL
);
841 if (!mimeinfo
->main
&& mimeinfo
->parent
)
843 filename
= mime_extract_file(source
, mimeinfo
);
845 compose_attach_append_with_type(compose
, filename
,
846 mimeinfo
->content_type
,
847 mimeinfo
->mime_type
);
852 if (mimeinfo
->sub
&& mimeinfo
->sub
->children
)
854 filename
= mime_extract_file(source
, mimeinfo
->sub
);
856 compose_attach_append_with_type(compose
, filename
,
857 mimeinfo
->content_type
,
858 mimeinfo
->sub
->mime_type
);
863 if (mimeinfo
->children
) {
866 child
= mimeinfo
->children
;
868 filename
= mime_extract_file(source
, child
);
870 compose_attach_append_with_type(compose
, filename
,
882 procmime_mimeinfo_free_all(mimeinfo
);
886 #define INSERT_FW_HEADER(var, hdr) \
887 if (msginfo->var && *msginfo->var) { \
888 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
889 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
890 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
893 Compose
* compose_forward(PrefsAccount
* account
, MsgInfo
*msginfo
,
897 /* PrefsAccount *account; */
902 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
903 g_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
905 if (account
== NULL
) {
906 account
= cur_account
;
908 account = msginfo->folder->folder->account;
909 if (!account) account = cur_account;
912 g_return_val_if_fail(account
!= NULL
, NULL
);
914 MSG_UNSET_PERM_FLAGS(msginfo
->flags
, MSG_REPLIED
);
915 MSG_SET_PERM_FLAGS(msginfo
->flags
, MSG_FORWARDED
);
916 CHANGE_FLAGS(msginfo
);
918 compose
= compose_create(account
);
919 compose
->mode
= COMPOSE_FORWARD
;
921 if (msginfo
->subject
&& *msginfo
->subject
) {
922 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Fw: ");
923 gtk_entry_append_text(GTK_ENTRY(compose
->subject_entry
),
927 text
= GTK_STEXT(compose
->text
);
928 gtk_stext_freeze(text
);
933 msgfile
= procmsg_get_message_file_path(msginfo
);
934 if (!is_file_exist(msgfile
))
935 g_warning(_("%s: file not exist\n"), msgfile
);
937 compose_attach_append(compose
, msgfile
,
938 MIME_MESSAGE_RFC822
);
945 if ((fp
= procmime_get_text_part(msginfo
)) == NULL
)
946 g_warning(_("Can't get text part\n"));
950 if (prefs_common
.fw_quotemark
&&
951 *prefs_common
.fw_quotemark
)
952 qmark
= prefs_common
.fw_quotemark
;
956 quote_str
= compose_quote_fmt(compose
, msginfo
,
957 prefs_common
.fw_quotefmt
,
960 if (quote_str
!= NULL
)
961 gtk_stext_insert(text
, NULL
, NULL
, NULL
,
967 compose_attach_parts(compose
, msginfo
);
970 if (prefs_common
.auto_sig
)
971 compose_insert_sig(compose
);
972 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
973 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
975 gtk_stext_thaw(text
);
976 if (account
->protocol
!= A_NNTP
)
977 gtk_widget_grab_focus(compose
->to_entry
);
979 gtk_widget_grab_focus(compose
->newsgroups_entry
);
984 #undef INSERT_FW_HEADER
986 Compose
* compose_forward_multiple(PrefsAccount
* account
,
987 GSList
*msginfo_list
)
994 g_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
996 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
997 if ( ((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1001 if (account
== NULL
) {
1002 account
= cur_account
;
1004 account = msginfo->folder->folder->account;
1005 if (!account) account = cur_account;
1008 g_return_val_if_fail(account
!= NULL
, NULL
);
1010 compose
= compose_create(account
);
1011 compose
->mode
= COMPOSE_FORWARD
;
1013 text
= GTK_STEXT(compose
->text
);
1014 gtk_stext_freeze(text
);
1016 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1017 msgfile
= procmsg_get_message_file_path((MsgInfo
*)msginfo
->data
);
1018 if (!is_file_exist(msgfile
))
1019 g_warning(_("%s: file not exist\n"), msgfile
);
1021 compose_attach_append(compose
, msgfile
,
1022 MIME_MESSAGE_RFC822
);
1026 if (prefs_common
.auto_sig
)
1027 compose_insert_sig(compose
);
1028 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
1029 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
1031 gtk_stext_thaw(text
);
1032 if (account
->protocol
!= A_NNTP
)
1033 gtk_widget_grab_focus(compose
->to_entry
);
1035 gtk_widget_grab_focus(compose
->newsgroups_entry
);
1040 void compose_reedit(MsgInfo
*msginfo
)
1043 PrefsAccount
*account
;
1046 gchar buf
[BUFFSIZE
];
1048 g_return_if_fail(msginfo
!= NULL
);
1049 g_return_if_fail(msginfo
->folder
!= NULL
);
1051 account
= msginfo
->folder
->folder
->account
;
1052 if (!account
) account
= cur_account
;
1053 g_return_if_fail(account
!= NULL
);
1055 compose
= compose_create(account
);
1056 compose
->mode
= COMPOSE_REEDIT_DRAFT
;
1057 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
1059 if (compose_parse_header(compose
, msginfo
) < 0) return;
1060 compose_reedit_set_entry(compose
, msginfo
);
1062 text
= GTK_STEXT(compose
->text
);
1063 gtk_stext_freeze(text
);
1065 if ((fp
= procmime_get_text_part(msginfo
)) == NULL
)
1066 g_warning(_("Can't get text part\n"));
1068 while (fgets(buf
, sizeof(buf
), fp
) != NULL
)
1069 gtk_stext_insert(text
, NULL
, NULL
, NULL
, buf
, -1);
1072 compose_attach_parts(compose
, msginfo
);
1074 gtk_stext_thaw(text
);
1075 gtk_widget_grab_focus(compose
->text
);
1078 GList
*compose_get_compose_list(void)
1080 return compose_list
;
1083 void compose_entry_append(Compose
*compose
, const gchar
*address
,
1084 ComposeEntryType type
)
1089 if (!address
|| *address
== '\0') return;
1093 entry
= GTK_ENTRY(compose
->cc_entry
);
1096 entry
= GTK_ENTRY(compose
->bcc_entry
);
1098 case COMPOSE_NEWSGROUPS
:
1099 entry
= GTK_ENTRY(compose
->newsgroups_entry
);
1103 entry
= GTK_ENTRY(compose
->to_entry
);
1107 text
= gtk_entry_get_text(entry
);
1109 gtk_entry_append_text(entry
, ", ");
1110 gtk_entry_append_text(entry
, address
);
1113 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
1115 static HeaderEntry hentry
[] = {{"Reply-To:", NULL
, TRUE
},
1116 {"Cc:", NULL
, FALSE
},
1117 {"References:", NULL
, FALSE
},
1118 {"Bcc:", NULL
, FALSE
},
1119 {"Newsgroups:", NULL
, FALSE
},
1120 {"Followup-To:", NULL
, FALSE
},
1121 {NULL
, NULL
, FALSE
}};
1135 g_return_val_if_fail(msginfo
!= NULL
, -1);
1137 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
1138 procheader_get_header_fields(fp
, hentry
);
1141 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
1142 conv_unmime_header_overwrite(hentry
[H_REPLY_TO
].body
);
1143 compose
->replyto
= hentry
[H_REPLY_TO
].body
;
1144 hentry
[H_REPLY_TO
].body
= NULL
;
1146 if (hentry
[H_CC
].body
!= NULL
) {
1147 conv_unmime_header_overwrite(hentry
[H_CC
].body
);
1148 compose
->cc
= hentry
[H_CC
].body
;
1149 hentry
[H_CC
].body
= NULL
;
1151 if (hentry
[H_REFERENCES
].body
!= NULL
) {
1152 if (compose
->mode
== COMPOSE_REEDIT_DRAFT
)
1153 compose
->references
= hentry
[H_REFERENCES
].body
;
1155 compose
->references
= compose_parse_references
1156 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
1157 g_free(hentry
[H_REFERENCES
].body
);
1159 hentry
[H_REFERENCES
].body
= NULL
;
1161 if (hentry
[H_BCC
].body
!= NULL
) {
1162 if (compose
->mode
== COMPOSE_REEDIT_DRAFT
) {
1163 conv_unmime_header_overwrite(hentry
[H_BCC
].body
);
1164 compose
->bcc
= hentry
[H_BCC
].body
;
1166 g_free(hentry
[H_BCC
].body
);
1167 hentry
[H_BCC
].body
= NULL
;
1169 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
1170 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
1171 hentry
[H_NEWSGROUPS
].body
= NULL
;
1173 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
1174 conv_unmime_header_overwrite(hentry
[H_FOLLOWUP_TO
].body
);
1175 compose
->followup_to
= hentry
[H_FOLLOWUP_TO
].body
;
1176 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
1179 if (compose
->mode
== COMPOSE_REEDIT_DRAFT
&& msginfo
->inreplyto
)
1180 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
1181 else if (compose
->mode
!= COMPOSE_REEDIT_DRAFT
&&
1182 msginfo
->msgid
&& *msginfo
->msgid
) {
1183 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
1185 if (!compose
->references
) {
1186 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
1187 compose
->references
=
1188 g_strdup_printf("<%s>\n\t<%s>",
1192 compose
->references
=
1193 g_strconcat("<", msginfo
->msgid
, ">",
1201 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
1203 GSList
*ref_id_list
, *cur
;
1207 ref_id_list
= references_list_append(NULL
, ref
);
1208 if (!ref_id_list
) return NULL
;
1209 if (msgid
&& *msgid
)
1210 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
1215 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
1216 /* "<" + Message-ID + ">" + CR+LF+TAB */
1217 len
+= strlen((gchar
*)cur
->data
) + 5;
1219 if (len
> MAX_REFERENCES_LEN
) {
1220 /* remove second message-ID */
1221 if (ref_id_list
&& ref_id_list
->next
&&
1222 ref_id_list
->next
->next
) {
1223 g_free(ref_id_list
->next
->data
);
1224 ref_id_list
= g_slist_remove
1225 (ref_id_list
, ref_id_list
->next
->data
);
1227 slist_free_strings(ref_id_list
);
1228 g_slist_free(ref_id_list
);
1235 new_ref
= g_string_new("");
1236 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
1237 if (new_ref
->len
> 0)
1238 g_string_append(new_ref
, "\n\t");
1239 g_string_sprintfa(new_ref
, "<%s>", (gchar
*)cur
->data
);
1242 slist_free_strings(ref_id_list
);
1243 g_slist_free(ref_id_list
);
1245 new_ref_str
= new_ref
->str
;
1246 g_string_free(new_ref
, FALSE
);
1252 static void compose_quote_file(Compose *compose, MsgInfo *msginfo, FILE *fp)
1254 GtkSText *text = GTK_STEXT(compose->text);
1257 GdkColor *qcolor = NULL;
1258 gchar buf[BUFFSIZE];
1260 gchar *linep, *cur, *leftp;
1261 gint line_len, cur_len;
1266 // if (prefs_common.enable_color) qcolor = "e_color;
1267 if (prefs_common.quotemark && *prefs_common.quotemark)
1268 qmark = prefs_common.quotemark;
1271 quote_str = compose_quote_parse_fmt(compose, msginfo, qmark);
1272 g_return_if_fail(quote_str != NULL);
1273 qlen = strlen(quote_str);
1275 if (!prefs_common.linewrap_quote ||
1276 prefs_common.linewrap_len <= qlen) {
1277 while (fgets(buf, sizeof(buf), fp) != NULL) {
1278 gtk_stext_insert(text, NULL, qcolor, NULL,
1280 gtk_stext_insert(text, NULL, qcolor, NULL, buf, -1);
1286 wrap_len = prefs_common.linewrap_len - qlen;
1288 while (fgets(buf, sizeof(buf), fp) != NULL) {
1290 str_len = strlen(buf);
1292 if (str_len <= wrap_len) {
1293 gtk_stext_insert(text, NULL, qcolor, NULL,
1295 gtk_stext_insert(text, NULL, qcolor, NULL, buf, -1);
1296 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
1300 linep = cur = leftp = buf;
1301 line_len = cur_len = 0;
1303 while (*cur != '\0') {
1304 ch_len = mblen(cur, MB_CUR_MAX);
1305 if (ch_len < 0) ch_len = 1;
1307 if (ch_len == 1 && isspace(*cur)) {
1308 linep = cur + ch_len;
1309 line_len = cur_len + ch_len;
1312 if (cur_len + ch_len > wrap_len && line_len > 0) {
1313 gtk_stext_insert(text, NULL, qcolor, NULL,
1316 if (isspace(*(linep - 1)))
1317 gtk_stext_insert(text, NULL,
1319 leftp, line_len - 1);
1321 gtk_stext_insert(text, NULL,
1324 gtk_stext_insert(text, NULL, NULL, NULL,
1328 cur_len = cur_len - line_len + ch_len;
1335 linep = cur + ch_len;
1336 line_len = cur_len + ch_len;
1343 gtk_stext_insert(text, NULL, qcolor, NULL,
1345 gtk_stext_insert(text, NULL, qcolor, NULL, leftp, -1);
1346 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1);
1355 static gchar *compose_quote_parse_fmt(Compose *compose, MsgInfo *msginfo,
1359 size_t buf_len = 1024;
1367 if (!fmt || *fmt == '\0') return 0;
1369 Xalloca(mbs, sizeof(wchar_t) + 1, return 0);
1370 Xalloca(wcsfmt, (strlen(fmt) + 1) * sizeof(wchar_t), return 0);
1371 mbstowcs(wcsfmt, fmt, strlen(fmt) + 1);
1374 ext_str = g_malloc(sizeof(gchar) * buf_len);
1375 g_return_val_if_fail(ext_str != NULL, NULL);
1380 len = wctomb(mbs, *sp);
1386 wctomb(mbs, *(++sp));
1391 str = msginfo->date;
1395 str = msginfo->from;
1399 if (!msginfo->fromname) {sp++; break;}
1400 p = msginfo->fromname;
1401 tmp[0] = tmp[1] = tmp[2] = '\0';
1403 if (*p && isalnum(*p))
1404 tmp[0] = toupper(*p);
1411 while (*p && !isspace(*p)) p++;
1412 while (*p && isspace(*p)) p++;
1413 if (*p && isalnum(*p))
1414 tmp[1] = toupper(*p);
1417 if (tmp[1]) str = tmp;
1421 str = msginfo->fromname;
1425 if (!msginfo->fromname) {sp++; break;}
1426 Xstrdup_a(str, msginfo->fromname,
1429 while (*p && !isspace(*p)) p++;
1434 str = msginfo->subject;
1446 if (!msginfo->msgid) {sp++; break;}
1447 Xalloca(str, strlen(msginfo->msgid) + 3,
1449 g_snprintf(str, strlen(msginfo->msgid) + 3,
1450 "<%s>", msginfo->msgid);
1462 while (ext_len + strlen(str) + 1 > buf_len)
1464 ext_str = g_realloc(ext_str,
1465 sizeof(gchar) * buf_len);
1466 g_return_val_if_fail(ext_str != NULL, NULL);
1467 strcpy(ext_str + ext_len, str);
1468 ext_len += strlen(str);
1470 } else if (*mbs == '\\') {
1471 wctomb(mbs, *(++sp));
1489 while (ext_len + strlen(str) + 1 > buf_len)
1491 ext_str = g_realloc(ext_str,
1492 sizeof(gchar) * buf_len);
1493 g_return_val_if_fail(ext_str != NULL, NULL);
1494 strcpy(ext_str + ext_len, str);
1495 ext_len += strlen(str);
1499 while (ext_len + len + 1 > buf_len) buf_len += 1024;
1500 ext_str = g_realloc(ext_str, sizeof(gchar) * buf_len);
1501 g_return_val_if_fail(ext_str != NULL, NULL);
1502 strcpy(ext_str + ext_len, mbs);
1509 ext_str = g_realloc(ext_str, strlen(ext_str) + 1);
1515 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
1516 gboolean to_all
, gboolean ignore_replyto
,
1517 gboolean followup_and_reply_to
)
1522 GHashTable
*to_table
;
1524 g_return_if_fail(compose
->account
!= NULL
);
1525 g_return_if_fail(msginfo
!= NULL
);
1527 if ((compose
->account
->protocol
!= A_NNTP
) || followup_and_reply_to
)
1528 gtk_entry_set_text(GTK_ENTRY(compose
->to_entry
),
1529 ( (compose
->replyto
&& !ignore_replyto
)
1531 : msginfo
->from
? msginfo
->from
: ""));
1532 if (compose
->account
->protocol
== A_NNTP
)
1533 gtk_entry_set_text(GTK_ENTRY(compose
->newsgroups_entry
),
1534 compose
->followup_to
? compose
->followup_to
1535 : compose
->newsgroups
? compose
->newsgroups
1538 if (msginfo
->subject
&& *msginfo
->subject
) {
1539 gchar
*buf
, *buf2
, *p
;
1541 buf
= g_strdup(msginfo
->subject
);
1542 while (!strncasecmp(buf
, "Re:", 3)) {
1544 while (isspace(*p
)) p
++;
1545 memmove(buf
, p
, strlen(p
) + 1);
1548 buf2
= g_strdup_printf("Re: %s", buf
);
1549 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1553 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
1555 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
1557 from
= g_strdup(compose
->replyto
? compose
->replyto
:
1558 msginfo
->from
? msginfo
->from
: "");
1559 extract_address(from
);
1561 cc_list
= address_list_append(NULL
, msginfo
->to
);
1562 cc_list
= address_list_append(cc_list
, compose
->cc
);
1564 to_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
1565 g_hash_table_insert(to_table
, from
, GINT_TO_POINTER(1));
1566 if (compose
->account
)
1567 g_hash_table_insert(to_table
, compose
->account
->address
,
1568 GINT_TO_POINTER(1));
1570 /* remove address on To: and that of current account */
1571 for (cur
= cc_list
; cur
!= NULL
; ) {
1572 GSList
*next
= cur
->next
;
1574 if (g_hash_table_lookup(to_table
, cur
->data
) != NULL
)
1575 cc_list
= g_slist_remove(cc_list
, cur
->data
);
1577 g_hash_table_insert(to_table
, cur
->data
, cur
);
1581 g_hash_table_destroy(to_table
);
1585 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
)
1586 compose_entry_append(compose
, (gchar
*)cur
->data
,
1588 slist_free_strings(cc_list
);
1589 g_slist_free(cc_list
);
1593 #define SET_ENTRY(entry, str) \
1596 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
1599 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
1601 g_return_if_fail(msginfo
!= NULL
);
1603 SET_ENTRY(to_entry
, msginfo
->to
);
1604 SET_ENTRY(subject_entry
, msginfo
->subject
);
1605 SET_ENTRY(cc_entry
, compose
->cc
);
1606 SET_ENTRY(bcc_entry
, compose
->bcc
);
1607 SET_ENTRY(reply_entry
, compose
->replyto
);
1610 GtkItemFactory
*ifactory
;
1611 GtkWidget
*menuitem
;
1613 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1614 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Bcc");
1615 gtk_check_menu_item_set_active
1616 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
1618 if (compose
->replyto
) {
1619 GtkItemFactory
*ifactory
;
1620 GtkWidget
*menuitem
;
1622 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1623 menuitem
= gtk_item_factory_get_item
1624 (ifactory
, "/Message/Reply to");
1625 gtk_check_menu_item_set_active
1626 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
1632 static void compose_exec_sig(Compose
*compose
, gchar
*sigfile
)
1639 size_t buf_len
= 128;
1641 if (strlen(sigfile
) < 2)
1644 sigprg
= popen(sigfile
+1, "r");
1647 buf
= g_malloc(buf_len
);
1650 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1651 "Unable to insert signature (malloc failed)\n", -1);
1657 while (!feof(sigprg
)) {
1658 bzero(buf
, buf_len
);
1659 fread(buf
, buf_len
-1, 1, sigprg
);
1660 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, buf
, -1);
1668 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1669 "Can't exec file: ", -1);
1670 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1675 static void compose_insert_sig(Compose
*compose
)
1679 if (compose
->account
&& compose
->account
->sig_path
)
1680 sigfile
= g_strdup(compose
->account
->sig_path
);
1682 sigfile
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
1683 DEFAULT_SIGNATURE
, NULL
);
1686 if (!is_file_or_fifo_exist(sigfile
) & (sigfile
[0] != '|')) {
1691 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, "\n\n", 2);
1692 if (prefs_common
.sig_sep
) {
1693 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
,
1694 prefs_common
.sig_sep
, -1);
1695 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
,
1699 if (sigfile
[0] == '|')
1701 compose_exec_sig(compose
, sigfile
);
1705 compose_insert_file(compose
, sigfile
);
1710 static void compose_insert_file(Compose
*compose
, const gchar
*file
)
1712 GtkSText
*text
= GTK_STEXT(compose
->text
);
1713 gchar buf
[BUFFSIZE
];
1716 g_return_if_fail(file
!= NULL
);
1718 if ((fp
= fopen(file
, "r")) == NULL
) {
1719 FILE_OP_ERROR(file
, "fopen");
1723 gtk_stext_freeze(text
);
1725 while (fgets(buf
, sizeof(buf
), fp
) != NULL
)
1726 gtk_stext_insert(text
, NULL
, NULL
, NULL
, buf
, -1);
1728 gtk_stext_thaw(text
);
1733 static void compose_attach_info(Compose
* compose
, AttachInfo
* ainfo
,
1734 ContentType cnttype
)
1736 gchar
*text
[N_ATTACH_COLS
];
1739 text
[COL_MIMETYPE
] = ainfo
->content_type
;
1740 text
[COL_SIZE
] = to_human_readable(ainfo
->size
);
1741 text
[COL_NAME
] = ainfo
->name
;
1743 row
= gtk_clist_append(GTK_CLIST(compose
->attach_clist
), text
);
1744 gtk_clist_set_row_data(GTK_CLIST(compose
->attach_clist
), row
, ainfo
);
1746 if (cnttype
!= MIME_MESSAGE_RFC822
)
1747 compose_changed_cb(NULL
, compose
);
1750 static void compose_attach_append_with_type(Compose
*compose
,
1753 ContentType cnttype
)
1758 if (!is_file_exist(file
)) {
1759 g_warning(_("File %s doesn't exist\n"), file
);
1762 if ((size
= get_file_size(file
)) < 0) {
1763 g_warning(_("Can't get file size of %s\n"), file
);
1767 alertpanel_notice(_("File %s is empty\n"), file
);
1771 if (!compose
->use_attach
) {
1772 GtkItemFactory
*ifactory
;
1773 GtkWidget
*menuitem
;
1775 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1776 menuitem
= gtk_item_factory_get_item(ifactory
,
1778 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
1782 ainfo
= g_new0(AttachInfo
, 1);
1783 ainfo
->file
= g_strdup(file
);
1785 if (cnttype
== MIME_MESSAGE_RFC822
) {
1786 ainfo
->encoding
= ENC_7BIT
;
1787 ainfo
->name
= g_strdup_printf(_("Message: %s"),
1790 ainfo
->encoding
= ENC_BASE64
;
1791 ainfo
->name
= g_strdup(g_basename(file
));
1794 ainfo
->content_type
= g_strdup(type
);
1797 compose_attach_info(compose
, ainfo
, cnttype
);
1800 static void compose_attach_append(Compose
*compose
, const gchar
*file
,
1801 ContentType cnttype
)
1804 gchar
*text
[N_ATTACH_COLS
];
1808 if (!is_file_exist(file
)) {
1809 g_warning(_("File %s doesn't exist\n"), file
);
1812 if ((size
= get_file_size(file
)) < 0) {
1813 g_warning(_("Can't get file size of %s\n"), file
);
1817 alertpanel_notice(_("File %s is empty\n"), file
);
1821 if (!compose
->use_attach
) {
1822 GtkItemFactory
*ifactory
;
1823 GtkWidget
*menuitem
;
1825 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1826 menuitem
= gtk_item_factory_get_item(ifactory
,
1828 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
1832 ainfo
= g_new0(AttachInfo
, 1);
1833 ainfo
->file
= g_strdup(file
);
1835 if (cnttype
== MIME_MESSAGE_RFC822
) {
1836 ainfo
->content_type
= g_strdup("message/rfc822");
1837 ainfo
->encoding
= ENC_7BIT
;
1838 ainfo
->name
= g_strdup_printf(_("Message: %s"),
1841 ainfo
->content_type
= procmime_get_mime_type(file
);
1842 if (!ainfo
->content_type
)
1843 ainfo
->content_type
=
1844 g_strdup("application/octet-stream");
1845 ainfo
->encoding
= ENC_BASE64
;
1846 ainfo
->name
= g_strdup(g_basename(file
));
1850 compose_attach_info(compose
, ainfo
, cnttype
);
1853 static void compose_wrap_line(Compose
*compose
)
1855 GtkSText
*text
= GTK_STEXT(compose
->text
);
1857 guint line_pos
= 0, cur_pos
= 0;
1858 gint line_len
= 0, cur_len
= 0;
1860 gchar cbuf
[MB_CUR_MAX
];
1862 gtk_stext_freeze(text
);
1864 text_len
= gtk_stext_get_length(text
);
1866 for (; cur_pos
< text_len
; cur_pos
++) {
1867 if (text
->use_wchar
)
1869 (cbuf
, (wchar_t)GTK_STEXT_INDEX(text
, cur_pos
));
1871 cbuf
[0] = GTK_STEXT_INDEX(text
, cur_pos
);
1875 if (ch_len
== 1 && *cbuf
== '\n') {
1876 line_pos
= cur_pos
+ 1;
1877 line_len
= cur_len
= 0;
1886 if (ch_len
== 1 && isspace(*cbuf
)) {
1887 line_pos
= cur_pos
+ 1;
1888 line_len
= cur_len
+ ch_len
;
1891 if (cur_len
+ ch_len
> prefs_common
.linewrap_len
&&
1895 if (text
->use_wchar
)
1896 tlen
= wctomb(cbuf
, (wchar_t)GTK_STEXT_INDEX(text
, line_pos
- 1));
1898 cbuf
[0] = GTK_STEXT_INDEX(text
, line_pos
- 1);
1901 if (tlen
== 1 && isspace(*cbuf
)) {
1902 gtk_stext_set_point(text
, line_pos
);
1903 gtk_stext_backward_delete(text
, 1);
1911 gtk_stext_set_point(text
, line_pos
);
1912 gtk_stext_insert(text
, NULL
, NULL
, NULL
, "\n", 1);
1916 cur_len
= cur_len
- line_len
+ ch_len
;
1922 line_pos
= cur_pos
+ 1;
1923 line_len
= cur_len
+ ch_len
;
1928 gtk_stext_thaw(text
);
1931 static void compose_set_title(Compose
*compose
)
1936 edited
= compose
->modified
? _(" [Edited]") : "";
1937 if (compose
->account
&& compose
->account
->address
)
1938 str
= g_strdup_printf(_("%s - Compose message%s"),
1939 compose
->account
->address
, edited
);
1941 str
= g_strdup_printf(_("Compose message%s"), edited
);
1942 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
1947 * compose_current_mail_account:
1949 * Find a current mail account (the currently selected account, or the
1950 * default account, if a news account is currently selected). If a
1951 * mail account cannot be found, display an error message.
1953 * Return value: Mail account, or NULL if not found.
1955 static PrefsAccount
*
1956 compose_current_mail_account(void)
1960 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
1963 ac
= account_get_default();
1964 if (!ac
|| ac
->protocol
== A_NNTP
) {
1965 alertpanel_error(_("Account for sending mail is not specified.\n"
1966 "Please select a mail account before sending."));
1973 gint
compose_send(Compose
*compose
)
1975 gchar tmp
[MAXPATHLEN
+ 1];
1976 gchar
*to
, *newsgroups
;
1978 static gboolean lock
= FALSE
;
1982 g_return_val_if_fail(compose
->account
!= NULL
, -1);
1983 g_return_val_if_fail(compose
->orig_account
!= NULL
, -1);
1987 to
= gtk_entry_get_text(GTK_ENTRY(compose
->to_entry
));
1988 newsgroups
= gtk_entry_get_text(GTK_ENTRY(compose
->newsgroups_entry
));
1989 if (*to
== '\0' && *newsgroups
== '\0') {
1990 alertpanel_error(_("Recipient is not specified."));
1995 /* write to temporary file */
1996 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg%d",
1997 get_rc_dir(), G_DIR_SEPARATOR
, (gint
)compose
);
1999 if (prefs_common
.linewrap_at_send
)
2000 compose_wrap_line(compose
);
2002 if (compose_write_to_file(compose
, tmp
, FALSE
) < 0) {
2007 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
2008 g_warning(_("can't get recipient list."));
2014 if (compose
->to_list
) {
2018 if (compose->account->protocol != A_NNTP)
2019 ac = compose->account;
2020 else if (compose->orig_account->protocol != A_NNTP)
2021 ac = compose->orig_account;
2022 else if (cur_account && cur_account->protocol != A_NNTP)
2025 ac = compose_current_mail_account();
2033 ac
= compose
->account
;
2035 ok
= send_message(tmp
, ac
, compose
->to_list
);
2036 statusbar_pop_all();
2039 if (ok
== 0 && compose
->newsgroup_list
) {
2042 if (compose
->account
->protocol
== A_NNTP
)
2043 folder
= FOLDER(compose
->account
->folder
);
2045 folder
= FOLDER(compose
->orig_account
->folder
);
2047 ok
= news_post(folder
, tmp
);
2049 alertpanel_error(_("Error occurred while posting the message to %s ."),
2050 compose
->account
->nntp_server
);
2057 /* queue message if failed to send */
2059 if (prefs_common
.queue_msg
) {
2064 _("Error occurred while sending the message.\n"
2065 "Put this message into queue folder?"),
2066 _("OK"), _("Cancel"), NULL
);
2067 if (G_ALERTDEFAULT
== val
) {
2068 ok
= compose_queue(compose
, tmp
);
2070 alertpanel_error(_("Can't queue the message."));
2073 alertpanel_error(_("Error occurred while sending the message."));
2076 /* save message to outbox */
2077 if (ok
== 0 && prefs_common
.savemsg
) {
2078 if (compose_save_to_outbox(compose
, tmp
) < 0)
2080 (_("Can't save the message to outbox."));
2088 static gint
compose_write_to_file(Compose
*compose
, const gchar
*file
,
2095 const gchar
*out_codeset
;
2096 EncodingType encoding
;
2098 if ((fp
= fopen(file
, "w")) == NULL
) {
2099 FILE_OP_ERROR(file
, "fopen");
2103 /* chmod for security */
2104 if (change_file_mode_rw(fp
, file
) < 0) {
2105 FILE_OP_ERROR(file
, "chmod");
2106 g_warning(_("can't change file mode\n"));
2109 /* get all composed text */
2110 chars
= gtk_editable_get_chars(GTK_EDITABLE(compose
->text
), 0, -1);
2111 len
= strlen(chars
);
2112 if (is_ascii_str(chars
)) {
2113 buf
= g_strdup(chars
);
2114 out_codeset
= "US-ASCII";
2115 encoding
= ENC_7BIT
;
2117 const gchar
*src_codeset
;
2119 out_codeset
= conv_get_outgoing_charset_str();
2120 if (!strcasecmp(out_codeset
, "US-ASCII"))
2121 out_codeset
= "ISO-8859-1";
2122 encoding
= procmime_get_encoding_for_charset(out_codeset
);
2123 debug_print("charset = %s, encoding = %s\n",
2124 out_codeset
, procmime_get_encoding_str(encoding
));
2126 src_codeset
= conv_get_current_charset_str();
2127 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
2132 alertpanel_error(_("Can't convert the codeset of the message."));
2139 if (compose_write_headers
2140 (compose
, fp
, out_codeset
, encoding
, is_draft
) < 0) {
2141 g_warning(_("can't write headers\n"));
2148 if (compose
->use_attach
) {
2150 /* This prolog message is ignored by mime software and
2151 * because it would make our signing/encryption task
2152 * tougher, we don't emit it in that case */
2153 if (!compose
->use_signing
&& !compose
->use_encryption
)
2155 fputs("This is a multi-part message in MIME format.\n", fp
);
2157 fprintf(fp
, "\n--%s\n", compose
->boundary
);
2158 fprintf(fp
, "Content-Type: text/plain; charset=%s\n",
2160 fprintf(fp
, "Content-Transfer-Encoding: %s\n",
2161 procmime_get_encoding_str(encoding
));
2167 if (encoding
== ENC_BASE64
) {
2168 gchar outbuf
[B64_BUFFSIZE
];
2171 for (i
= 0; i
< len
; i
+= B64_LINE_SIZE
) {
2172 l
= MIN(B64_LINE_SIZE
, len
- i
);
2173 to64frombits(outbuf
, buf
+ i
, l
);
2177 } else if (fwrite(buf
, sizeof(gchar
), len
, fp
) != len
) {
2178 FILE_OP_ERROR(file
, "fwrite");
2186 if (compose
->use_attach
)
2187 compose_write_attach(compose
, fp
);
2189 if (fclose(fp
) == EOF
) {
2190 FILE_OP_ERROR(file
, "fclose");
2196 if (compose
->use_signing
) {
2197 if (rfc2015_sign(file
, compose
->account
) < 0) {
2202 if (compose
->use_encryption
) {
2203 if (rfc2015_encrypt(file
, compose
->to_list
) < 0) {
2208 #endif /* USE_GPGME */
2213 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
2219 if ((fp
= fopen(file
, "w")) == NULL
) {
2220 FILE_OP_ERROR(file
, "fopen");
2224 /* chmod for security */
2225 if (change_file_mode_rw(fp
, file
) < 0) {
2226 FILE_OP_ERROR(file
, "chmod");
2227 g_warning(_("can't change file mode\n"));
2230 chars
= gtk_editable_get_chars(GTK_EDITABLE(compose
->text
), 0, -1);
2233 len
= strlen(chars
);
2234 if (fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
2235 FILE_OP_ERROR(file
, "fwrite");
2244 if (fclose(fp
) == EOF
) {
2245 FILE_OP_ERROR(file
, "fclose");
2252 static gint
compose_save_to_outbox(Compose
*compose
, const gchar
*file
)
2259 debug_print(_("saving sent message...\n"));
2261 outbox
= folder_get_default_outbox();
2262 path
= folder_item_get_path(outbox
);
2263 if (!is_dir_exist(path
))
2264 make_dir_hier(path
);
2266 folder_item_scan(outbox
);
2267 if ((num
= folder_item_add_msg(outbox
, file
, FALSE
)) < 0) {
2269 g_warning(_("can't save message\n"));
2273 if ((fp
= procmsg_open_mark_file(path
, TRUE
)) == NULL
)
2274 g_warning(_("can't open mark file\n"));
2278 newmsginfo
.msgnum
= num
;
2279 newmsginfo
.flags
.perm_flags
= 0;
2280 newmsginfo
.flags
.tmp_flags
= 0;
2281 procmsg_write_flags(&newmsginfo
, fp
);
2289 static gint
compose_queue(Compose
*compose
, const gchar
*file
)
2292 gchar
*tmp
, *queue_path
;
2295 gchar buf
[BUFFSIZE
];
2298 debug_print(_("queueing message...\n"));
2299 g_return_val_if_fail(compose
->to_list
!= NULL
, -1);
2300 g_return_val_if_fail(compose
->account
!= NULL
, -1);
2302 tmp
= g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
2303 G_DIR_SEPARATOR
, (gint
)compose
);
2304 if ((fp
= fopen(tmp
, "w")) == NULL
) {
2305 FILE_OP_ERROR(tmp
, "fopen");
2309 if ((src_fp
= fopen(file
, "r")) == NULL
) {
2310 FILE_OP_ERROR(file
, "fopen");
2316 if (change_file_mode_rw(fp
, tmp
) < 0) {
2317 FILE_OP_ERROR(tmp
, "chmod");
2318 g_warning(_("can't change file mode\n"));
2321 /* queueing variables */
2322 fprintf(fp
, "AF:\n");
2323 fprintf(fp
, "NF:0\n");
2324 fprintf(fp
, "PS:10\n");
2325 fprintf(fp
, "SRH:1\n");
2326 fprintf(fp
, "SFN:\n");
2327 fprintf(fp
, "DSR:\n");
2329 fprintf(fp
, "MID:<%s>\n", compose
->msgid
);
2331 fprintf(fp
, "MID:\n");
2332 fprintf(fp
, "CFG:\n");
2333 fprintf(fp
, "PT:0\n");
2334 fprintf(fp
, "S:%s\n", compose
->account
->address
);
2335 fprintf(fp
, "RQ:\n");
2336 if (compose
->account
->smtp_server
)
2337 fprintf(fp
, "SSV:%s\n", compose
->account
->smtp_server
);
2339 fprintf(fp
, "SSV:\n");
2340 if (compose
->account
->nntp_server
)
2341 fprintf(fp
, "NSV:%s\n", compose
->account
->nntp_server
);
2343 fprintf(fp
, "NSV:\n");
2344 fprintf(fp
, "SSH:\n");
2345 fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
);
2346 for (cur
= compose
->to_list
->next
; cur
!= NULL
; cur
= cur
->next
)
2347 fprintf(fp
, ",<%s>", (gchar
*)cur
->data
);
2351 while (fgets(buf
, sizeof(buf
), src_fp
) != NULL
) {
2352 if (fputs(buf
, fp
) == EOF
) {
2353 FILE_OP_ERROR(tmp
, "fputs");
2363 if (fclose(fp
) == EOF
) {
2364 FILE_OP_ERROR(tmp
, "fclose");
2370 queue
= folder_get_default_queue();
2371 folder_item_scan(queue
);
2372 queue_path
= folder_item_get_path(queue
);
2373 if (!is_dir_exist(queue_path
))
2374 make_dir_hier(queue_path
);
2375 if ((num
= folder_item_add_msg(queue
, tmp
, TRUE
)) < 0) {
2376 g_warning(_("can't queue the message\n"));
2384 if ((fp
= procmsg_open_mark_file(queue_path
, TRUE
)) == NULL
)
2385 g_warning(_("can't open mark file\n"));
2389 newmsginfo
.msgnum
= num
;
2390 newmsginfo
.flags
.perm_flags
= 0;
2391 newmsginfo
.flags
.tmp_flags
= 0;
2392 procmsg_write_flags(&newmsginfo
, fp
);
2397 folder_item_scan(queue
);
2398 folderview_update_item(queue
, TRUE
);
2403 static void compose_write_attach(Compose
*compose
, FILE *fp
)
2406 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
2409 gchar filename
[BUFFSIZE
];
2412 for (row
= 0; (ainfo
= gtk_clist_get_row_data(clist
, row
)) != NULL
;
2414 if ((attach_fp
= fopen(ainfo
->file
, "r")) == NULL
) {
2415 g_warning(_("Can't open file %s\n"), ainfo
->file
);
2419 fprintf(fp
, "\n--%s\n", compose
->boundary
);
2421 if (!strcmp2(ainfo
->content_type
, "message/rfc822")) {
2422 fprintf(fp
, "Content-Type: %s\n", ainfo
->content_type
);
2423 fprintf(fp
, "Content-Disposition: inline\n");
2425 conv_encode_header(filename
, sizeof(filename
),
2427 fprintf(fp
, "Content-Type: %s;\n"
2429 ainfo
->content_type
, filename
);
2430 fprintf(fp
, "Content-Disposition: attachment;\n"
2431 " filename=\"%s\"\n", filename
);
2434 fprintf(fp
, "Content-Transfer-Encoding: %s\n\n",
2435 procmime_get_encoding_str(ainfo
->encoding
));
2437 if (ainfo
->encoding
== ENC_7BIT
) {
2438 gchar buf
[BUFFSIZE
];
2440 while (fgets(buf
, sizeof(buf
), attach_fp
) != NULL
) {
2442 if (len
> 1 && buf
[len
- 1] == '\n' &&
2443 buf
[len
- 2] == '\r') {
2444 buf
[len
- 2] = '\n';
2445 buf
[len
- 1] = '\0';
2450 gchar inbuf
[B64_LINE_SIZE
], outbuf
[B64_BUFFSIZE
];
2452 while ((len
= fread(inbuf
, sizeof(gchar
),
2453 B64_LINE_SIZE
, attach_fp
))
2455 to64frombits(outbuf
, inbuf
, B64_LINE_SIZE
);
2459 if (len
> 0 && feof(attach_fp
)) {
2460 to64frombits(outbuf
, inbuf
, len
);
2469 fprintf(fp
, "\n--%s--\n", compose
->boundary
);
2472 #define IS_IN_CUSTOM_HEADER(header) \
2473 (compose->account->add_customhdr && \
2474 custom_header_find(compose->account->customhdr_list, header) != NULL)
2476 static gint
compose_write_headers(Compose
*compose
, FILE *fp
,
2477 const gchar
*charset
, EncodingType encoding
,
2480 gchar buf
[BUFFSIZE
];
2482 /* struct utsname utsbuf; */
2484 g_return_val_if_fail(fp
!= NULL
, -1);
2485 g_return_val_if_fail(charset
!= NULL
, -1);
2486 g_return_val_if_fail(compose
->account
!= NULL
, -1);
2487 g_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
2490 if (compose
->account
->add_date
) {
2491 get_rfc822_date(buf
, sizeof(buf
));
2492 fprintf(fp
, "Date: %s\n", buf
);
2496 if (!IS_IN_CUSTOM_HEADER("From")) {
2497 if (compose
->account
->name
&& *compose
->account
->name
) {
2498 compose_convert_header
2499 (buf
, sizeof(buf
), compose
->account
->name
,
2501 fprintf(fp
, "From: %s <%s>\n",
2502 buf
, compose
->account
->address
);
2504 fprintf(fp
, "From: %s\n", compose
->account
->address
);
2507 slist_free_strings(compose
->to_list
);
2508 g_slist_free(compose
->to_list
);
2509 compose
->to_list
= NULL
;
2512 if (compose
->use_to
) {
2513 str
= gtk_entry_get_text(GTK_ENTRY(compose
->to_entry
));
2515 Xstrdup_a(str
, str
, return -1);
2518 compose
->to_list
= address_list_append
2519 (compose
->to_list
, str
);
2520 if (!IS_IN_CUSTOM_HEADER("To")) {
2521 compose_convert_header
2522 (buf
, sizeof(buf
), str
,
2524 fprintf(fp
, "To: %s\n", buf
);
2530 slist_free_strings(compose
->newsgroup_list
);
2531 g_slist_free(compose
->newsgroup_list
);
2532 compose
->newsgroup_list
= NULL
;
2535 str
= gtk_entry_get_text(GTK_ENTRY(compose
->newsgroups_entry
));
2537 Xstrdup_a(str
, str
, return -1);
2541 compose
->newsgroup_list
=
2542 newsgroup_list_append(compose
->newsgroup_list
,
2544 if (!IS_IN_CUSTOM_HEADER("Newsgroups")) {
2545 compose_convert_header(buf
, sizeof(buf
), str
,
2546 strlen("Newsgroups: "));
2547 fprintf(fp
, "Newsgroups: %s\n", buf
);
2552 if (!is_draft
&& !compose
->to_list
&& !compose
->newsgroup_list
)
2556 if (compose
->use_cc
) {
2557 str
= gtk_entry_get_text(GTK_ENTRY(compose
->cc_entry
));
2559 Xstrdup_a(str
, str
, return -1);
2562 compose
->to_list
= address_list_append
2563 (compose
->to_list
, str
);
2564 if (!IS_IN_CUSTOM_HEADER("Cc")) {
2565 compose_convert_header
2566 (buf
, sizeof(buf
), str
,
2568 fprintf(fp
, "Cc: %s\n", buf
);
2575 if (compose
->use_bcc
) {
2576 str
= gtk_entry_get_text(GTK_ENTRY(compose
->bcc_entry
));
2578 Xstrdup_a(str
, str
, return -1);
2581 compose
->to_list
= address_list_append
2582 (compose
->to_list
, str
);
2584 compose_convert_header
2585 (buf
, sizeof(buf
), str
,
2587 fprintf(fp
, "Bcc: %s\n", buf
);
2594 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
2595 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
2596 Xstrdup_a(str
, str
, return -1);
2599 compose_convert_header(buf
, sizeof(buf
), str
,
2600 strlen("Subject: "));
2601 fprintf(fp
, "Subject: %s\n", buf
);
2606 if (compose
->account
->gen_msgid
) {
2607 compose_generate_msgid(compose
, buf
, sizeof(buf
));
2608 fprintf(fp
, "Message-Id: <%s>\n", buf
);
2609 compose
->msgid
= g_strdup(buf
);
2613 if (compose
->inreplyto
&& compose
->to_list
)
2614 fprintf(fp
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
2617 if (compose
->references
)
2618 fprintf(fp
, "References: %s\n", compose
->references
);
2621 if (compose
->use_followupto
&& !IS_IN_CUSTOM_HEADER("Followup-To")) {
2622 str
= gtk_entry_get_text(GTK_ENTRY(compose
->followup_entry
));
2624 Xstrdup_a(str
, str
, return -1);
2628 compose_convert_header(buf
, sizeof(buf
), str
,
2629 strlen("Followup-To: "));
2630 fprintf(fp
, "Followup-To: %s\n", buf
);
2636 if (compose
->use_replyto
&& !IS_IN_CUSTOM_HEADER("Reply-To")) {
2637 str
= gtk_entry_get_text(GTK_ENTRY(compose
->reply_entry
));
2639 Xstrdup_a(str
, str
, return -1);
2642 compose_convert_header(buf
, sizeof(buf
), str
,
2643 strlen("Reply-To: "));
2644 fprintf(fp
, "Reply-To: %s\n", buf
);
2650 if (compose
->account
->organization
&&
2651 !IS_IN_CUSTOM_HEADER("Organization")) {
2652 compose_convert_header(buf
, sizeof(buf
),
2653 compose
->account
->organization
,
2654 strlen("Organization: "));
2655 fprintf(fp
, "Organization: %s\n", buf
);
2658 /* Program version and system info */
2659 /* uname(&utsbuf); */
2660 str
= gtk_entry_get_text(GTK_ENTRY(compose
->to_entry
));
2661 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
2662 fprintf(fp
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
2664 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
2666 /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
2668 str
= gtk_entry_get_text(GTK_ENTRY(compose
->newsgroups_entry
));
2669 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
2670 fprintf(fp
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
2672 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
2674 /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
2677 /* custom headers */
2678 if (compose
->account
->add_customhdr
) {
2681 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
2683 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
2685 if (strcasecmp(chdr
->name
, "Date") != 0 &&
2686 strcasecmp(chdr
->name
, "From") != 0 &&
2687 strcasecmp(chdr
->name
, "To") != 0 &&
2688 strcasecmp(chdr
->name
, "Sender") != 0 &&
2689 strcasecmp(chdr
->name
, "Message-Id") != 0 &&
2690 strcasecmp(chdr
->name
, "In-Reply-To") != 0 &&
2691 strcasecmp(chdr
->name
, "References") != 0 &&
2692 strcasecmp(chdr
->name
, "Mime-Version") != 0 &&
2693 strcasecmp(chdr
->name
, "Content-Type") != 0 &&
2694 strcasecmp(chdr
->name
, "Content-Transfer-Encoding")
2696 compose_convert_header
2698 chdr
->value
? chdr
->value
: "",
2699 strlen(chdr
->name
) + 2);
2700 fprintf(fp
, "%s: %s\n", chdr
->name
, buf
);
2705 fprintf(fp
, "Mime-Version: 1.0\n");
2706 if (compose
->use_attach
) {
2707 get_rfc822_date(buf
, sizeof(buf
));
2708 subst_char(buf
, ' ', '_');
2709 subst_char(buf
, ',', '_');
2710 compose
->boundary
= g_strdup_printf("Multipart_%s_%08x",
2711 buf
, (guint
)compose
);
2713 "Content-Type: multipart/mixed;\n"
2714 " boundary=\"%s\"\n", compose
->boundary
);
2716 fprintf(fp
, "Content-Type: text/plain; charset=%s\n", charset
);
2717 fprintf(fp
, "Content-Transfer-Encoding: %s\n",
2718 procmime_get_encoding_str(encoding
));
2721 /* Request Return Receipt */
2722 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
2723 if (compose
->return_receipt
) {
2724 if (compose
->account
->name
2725 && *compose
->account
->name
) {
2726 compose_convert_header(buf
, sizeof(buf
), compose
->account
->name
, strlen("Disposition-Notification-To: "));
2727 fprintf(fp
, "Disposition-Notification-To: %s <%s>\n", buf
, compose
->account
->address
);
2729 fprintf(fp
, "Disposition-Notification-To: %s\n", compose
->account
->address
);
2733 /* separator between header and body */
2739 #undef IS_IN_CUSTOM_HEADER
2741 static void compose_convert_header(gchar
*dest
, gint len
, gchar
*src
,
2744 g_return_if_fail(src
!= NULL
);
2745 g_return_if_fail(dest
!= NULL
);
2747 if (len
< 1) return;
2751 if (is_ascii_str(src
)) {
2752 strncpy2(dest
, src
, len
);
2753 dest
[len
- 1] = '\0';
2756 conv_encode_header(dest
, len
, src
, header_len
);
2759 static void compose_generate_msgid(Compose
*compose
, gchar
*buf
, gint len
)
2768 if (compose
->account
&& compose
->account
->address
&&
2769 *compose
->account
->address
) {
2770 if (strchr(compose
->account
->address
, '@'))
2771 addr
= g_strdup(compose
->account
->address
);
2773 addr
= g_strconcat(compose
->account
->address
, "@",
2774 get_domain_name(), NULL
);
2776 addr
= g_strconcat(g_get_user_name(), "@", get_domain_name(),
2779 g_snprintf(buf
, len
, "%04d%02d%02d%02d%02d%02d.%08x.%s",
2780 lt
->tm_year
+ 1900, lt
->tm_mon
+ 1,
2781 lt
->tm_mday
, lt
->tm_hour
,
2782 lt
->tm_min
, lt
->tm_sec
,
2783 (guint
)random(), addr
);
2785 debug_print(_("generated Message-ID: %s\n"), buf
);
2790 static void compose_add_entry_field(GtkWidget
*table
, GtkWidget
**hbox
,
2791 GtkWidget
**entry
, gint
*count
,
2792 const gchar
*label_str
,
2793 gboolean is_addr_entry
)
2797 if (GTK_TABLE(table
)->nrows
< (*count
) + 1)
2798 gtk_table_resize(GTK_TABLE(table
), (*count
) + 1, 2);
2800 *hbox
= gtk_hbox_new(FALSE
, 0);
2801 label
= gtk_label_new
2802 (prefs_common
.trans_hdr
? gettext(label_str
) : label_str
);
2803 gtk_box_pack_end(GTK_BOX(*hbox
), label
, FALSE
, FALSE
, 0);
2804 gtk_table_attach(GTK_TABLE(table
), *hbox
, 0, 1, *count
, (*count
) + 1,
2806 *entry
= gtk_entry_new();
2807 gtk_table_attach_defaults
2808 (GTK_TABLE(table
), *entry
, 1, 2, *count
, (*count
) + 1);
2809 if (GTK_TABLE(table
)->nrows
> (*count
) + 1)
2810 gtk_table_set_row_spacing(GTK_TABLE(table
), *count
, 4);
2813 address_completion_register_entry(GTK_ENTRY(*entry
));
2818 static Compose
*compose_create(PrefsAccount
*account
)
2824 GtkWidget
*handlebox
;
2828 GtkWidget
*table_vbox
;
2830 GtkWidget
*from_optmenu_hbox
;
2831 GtkWidget
*to_entry
;
2833 GtkWidget
*newsgroups_entry
;
2834 GtkWidget
*newsgroups_hbox
;
2835 GtkWidget
*subject_entry
;
2836 GtkWidget
*cc_entry
;
2838 GtkWidget
*bcc_entry
;
2839 GtkWidget
*bcc_hbox
;
2840 GtkWidget
*reply_entry
;
2841 GtkWidget
*reply_hbox
;
2842 GtkWidget
*followup_entry
;
2843 GtkWidget
*followup_hbox
;
2847 GtkWidget
*attach_scrwin
;
2848 GtkWidget
*attach_clist
;
2850 GtkWidget
*edit_vbox
;
2851 GtkWidget
*ruler_hbox
;
2853 GtkWidget
*scrolledwin
;
2859 gchar
*titles
[] = {_("MIME type"), _("Size"), _("Name")};
2860 guint n_menu_entries
;
2861 GtkStyle
*style
, *new_style
;
2864 gboolean success
[1];
2866 GtkWidget
*popupmenu
;
2867 GtkWidget
*menuitem
;
2868 GtkItemFactory
*popupfactory
;
2869 GtkItemFactory
*ifactory
;
2874 g_return_val_if_fail(account
!= NULL
, NULL
);
2876 debug_print(_("Creating compose window...\n"));
2877 compose
= g_new0(Compose
, 1);
2879 compose
->account
= account
;
2880 compose
->orig_account
= account
;
2882 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
2883 gtk_window_set_policy(GTK_WINDOW(window
), TRUE
, TRUE
, FALSE
);
2884 gtk_widget_set_usize(window
, -1, prefs_common
.compose_height
);
2885 gtk_signal_connect(GTK_OBJECT(window
), "delete_event",
2886 GTK_SIGNAL_FUNC(compose_delete_cb
), compose
);
2887 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
2888 GTK_SIGNAL_FUNC(compose_destroy_cb
), compose
);
2889 gtk_signal_connect(GTK_OBJECT(window
), "focus_in_event",
2890 GTK_SIGNAL_FUNC(manage_window_focus_in
), NULL
);
2891 gtk_signal_connect(GTK_OBJECT(window
), "focus_out_event",
2892 GTK_SIGNAL_FUNC(manage_window_focus_out
), NULL
);
2893 gtk_widget_realize(window
);
2895 vbox
= gtk_vbox_new(FALSE
, 0);
2896 gtk_container_add(GTK_CONTAINER(window
), vbox
);
2898 n_menu_entries
= sizeof(compose_entries
) / sizeof(compose_entries
[0]);
2899 menubar
= menubar_create(window
, compose_entries
,
2900 n_menu_entries
, "<Compose>", compose
);
2901 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
2903 handlebox
= gtk_handle_box_new();
2904 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
2906 compose_toolbar_create(compose
, handlebox
);
2908 vbox2
= gtk_vbox_new(FALSE
, 2);
2909 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
2910 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), BORDER_WIDTH
);
2912 table_vbox
= gtk_vbox_new(FALSE
, 0);
2913 gtk_box_pack_start(GTK_BOX(vbox2
), table_vbox
, FALSE
, TRUE
, 0);
2914 gtk_container_set_border_width(GTK_CONTAINER(table_vbox
),
2917 table
= gtk_table_new(8, 2, FALSE
);
2918 gtk_box_pack_start(GTK_BOX(table_vbox
), table
, FALSE
, TRUE
, 0);
2920 /* option menu for selecting accounts */
2921 hbox
= gtk_hbox_new(FALSE
, 0);
2922 label
= gtk_label_new(prefs_common
.trans_hdr
? _("From:") : "From:");
2923 gtk_box_pack_end(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
2924 gtk_table_attach(GTK_TABLE(table
), hbox
, 0, 1, count
, count
+ 1,
2926 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
2927 gtk_table_attach_defaults(GTK_TABLE(table
), from_optmenu_hbox
,
2928 1, 2, count
, count
+ 1);
2929 gtk_table_set_row_spacing(GTK_TABLE(table
), 0, 4);
2932 /* header labels and entries */
2933 compose_add_entry_field(table
, &to_hbox
, &to_entry
, &count
,
2935 gtk_table_set_row_spacing(GTK_TABLE(table
), 0, 4);
2936 compose_add_entry_field(table
, &newsgroups_hbox
, &newsgroups_entry
,
2937 &count
, "Newsgroups:", FALSE
);
2938 gtk_table_set_row_spacing(GTK_TABLE(table
), 1, 4);
2939 compose_add_entry_field(table
, &hbox
, &subject_entry
, &count
,
2941 gtk_table_set_row_spacing(GTK_TABLE(table
), 2, 4);
2942 compose_add_entry_field(table
, &cc_hbox
, &cc_entry
, &count
,
2944 gtk_table_set_row_spacing(GTK_TABLE(table
), 3, 4);
2945 compose_add_entry_field(table
, &bcc_hbox
, &bcc_entry
, &count
,
2947 gtk_table_set_row_spacing(GTK_TABLE(table
), 4, 4);
2948 compose_add_entry_field(table
, &reply_hbox
, &reply_entry
, &count
,
2950 gtk_table_set_row_spacing(GTK_TABLE(table
), 5, 4);
2951 compose_add_entry_field(table
, &followup_hbox
, &followup_entry
, &count
,
2952 "Followup-To:", FALSE
);
2953 gtk_table_set_row_spacing(GTK_TABLE(table
), 6, 4);
2955 gtk_table_set_col_spacings(GTK_TABLE(table
), 4);
2957 gtk_signal_connect(GTK_OBJECT(to_entry
), "activate",
2958 GTK_SIGNAL_FUNC(to_activated
), compose
);
2959 gtk_signal_connect(GTK_OBJECT(newsgroups_entry
), "activate",
2960 GTK_SIGNAL_FUNC(newsgroups_activated
), compose
);
2961 gtk_signal_connect(GTK_OBJECT(subject_entry
), "activate",
2962 GTK_SIGNAL_FUNC(subject_activated
), compose
);
2963 gtk_signal_connect(GTK_OBJECT(cc_entry
), "activate",
2964 GTK_SIGNAL_FUNC(cc_activated
), compose
);
2965 gtk_signal_connect(GTK_OBJECT(bcc_entry
), "activate",
2966 GTK_SIGNAL_FUNC(bcc_activated
), compose
);
2967 gtk_signal_connect(GTK_OBJECT(reply_entry
), "activate",
2968 GTK_SIGNAL_FUNC(replyto_activated
), compose
);
2969 gtk_signal_connect(GTK_OBJECT(followup_entry
), "activate",
2970 GTK_SIGNAL_FUNC(followupto_activated
), compose
);
2972 gtk_signal_connect(GTK_OBJECT(subject_entry
), "grab_focus",
2973 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2974 gtk_signal_connect(GTK_OBJECT(to_entry
), "grab_focus",
2975 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2976 gtk_signal_connect(GTK_OBJECT(newsgroups_entry
), "grab_focus",
2977 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2978 gtk_signal_connect(GTK_OBJECT(cc_entry
), "grab_focus",
2979 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2980 gtk_signal_connect(GTK_OBJECT(bcc_entry
), "grab_focus",
2981 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2982 gtk_signal_connect(GTK_OBJECT(reply_entry
), "grab_focus",
2983 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2984 gtk_signal_connect(GTK_OBJECT(followup_entry
), "grab_focus",
2985 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
2987 /* attachment list */
2988 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
2989 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
2990 GTK_POLICY_AUTOMATIC
,
2992 gtk_widget_set_usize(attach_scrwin
, -1, 80);
2994 attach_clist
= gtk_clist_new_with_titles(N_ATTACH_COLS
, titles
);
2995 gtk_clist_set_column_justification(GTK_CLIST(attach_clist
), COL_SIZE
,
2997 gtk_clist_set_column_width(GTK_CLIST(attach_clist
), COL_MIMETYPE
, 240);
2998 gtk_clist_set_column_width(GTK_CLIST(attach_clist
), COL_SIZE
, 64);
2999 gtk_clist_set_selection_mode(GTK_CLIST(attach_clist
),
3000 GTK_SELECTION_EXTENDED
);
3001 for (i
= 0; i
< N_ATTACH_COLS
; i
++)
3002 GTK_WIDGET_UNSET_FLAGS
3003 (GTK_CLIST(attach_clist
)->column
[i
].button
,
3005 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
3007 gtk_signal_connect(GTK_OBJECT(attach_clist
), "select_row",
3008 GTK_SIGNAL_FUNC(attach_selected
), compose
);
3009 gtk_signal_connect(GTK_OBJECT(attach_clist
), "button_press_event",
3010 GTK_SIGNAL_FUNC(attach_button_pressed
), compose
);
3011 gtk_signal_connect(GTK_OBJECT(attach_clist
), "key_press_event",
3012 GTK_SIGNAL_FUNC(attach_key_pressed
), compose
);
3015 gtk_drag_dest_set(attach_clist
,
3016 GTK_DEST_DEFAULT_ALL
, compose_mime_types
, 1,
3018 gtk_signal_connect(GTK_OBJECT(attach_clist
), "drag_data_received",
3019 GTK_SIGNAL_FUNC(compose_attach_drag_received_cb
),
3022 /* pane between attach clist and text */
3023 paned
= gtk_vpaned_new();
3024 gtk_paned_add1(GTK_PANED(paned
), attach_scrwin
);
3025 gtk_widget_ref(paned
);
3026 gtk_widget_show_all(paned
);
3028 edit_vbox
= gtk_vbox_new(FALSE
, 0);
3029 gtk_box_pack_start(GTK_BOX(vbox2
), edit_vbox
, TRUE
, TRUE
, 0);
3032 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
3033 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
3035 ruler
= gtk_shruler_new();
3036 gtk_ruler_set_range(GTK_RULER(ruler
), 0.0, 100.0, 1.0, 100.0);
3037 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
3039 gtk_widget_set_usize(ruler_hbox
, 1, -1);
3042 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
3043 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
3044 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
3045 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
3046 gtk_widget_set_usize(scrolledwin
, prefs_common
.compose_width
, -1);
3048 text
= gtk_stext_new(gtk_scrolled_window_get_hadjustment
3049 (GTK_SCROLLED_WINDOW(scrolledwin
)),
3050 gtk_scrolled_window_get_vadjustment
3051 (GTK_SCROLLED_WINDOW(scrolledwin
)));
3052 GTK_STEXT(text
)->default_tab_width
= 8;
3053 gtk_stext_set_editable(GTK_STEXT(text
), TRUE
);
3054 if (prefs_common
.smart_wrapping
) {
3055 gtk_stext_set_word_wrap(GTK_STEXT(text
), TRUE
);
3056 gtk_stext_set_wrap_rmargin(GTK_STEXT(text
), prefs_common
.linewrap_len
);
3059 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
3061 gtk_signal_connect(GTK_OBJECT(text
), "changed",
3062 GTK_SIGNAL_FUNC(compose_changed_cb
), compose
);
3063 gtk_signal_connect(GTK_OBJECT(text
), "grab_focus",
3064 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
3065 gtk_signal_connect_after(GTK_OBJECT(text
), "button_press_event",
3066 GTK_SIGNAL_FUNC(compose_button_press_cb
),
3069 gtk_signal_connect_after(GTK_OBJECT(text
), "key_press_event",
3070 GTK_SIGNAL_FUNC(compose_key_press_cb
),
3073 gtk_signal_connect_after(GTK_OBJECT(text
), "size_allocate",
3074 GTK_SIGNAL_FUNC(compose_edit_size_alloc
),
3078 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
, 1,
3080 gtk_signal_connect(GTK_OBJECT(text
), "drag_data_received",
3081 GTK_SIGNAL_FUNC(compose_insert_drag_received_cb
),
3084 gtk_widget_show_all(vbox
);
3086 style
= gtk_widget_get_style(text
);
3088 /* workaround for the slow down of GtkText when using Pixmap theme */
3089 if (style
->engine
) {
3090 GtkThemeEngine
*engine
;
3092 engine
= style
->engine
;
3093 style
->engine
= NULL
;
3094 new_style
= gtk_style_copy(style
);
3095 style
->engine
= engine
;
3097 new_style
= gtk_style_copy(style
);
3099 if (prefs_common
.textfont
) {
3102 charset
= conv_get_current_charset();
3103 if (MB_CUR_MAX
== 1) {
3106 Xstrdup_a(fontstr
, prefs_common
.textfont
, );
3107 if (fontstr
&& (p
= strchr(fontstr
, ',')) != NULL
)
3109 font
= gdk_font_load(fontstr
);
3111 font
= gdk_fontset_load(prefs_common
.textfont
);
3113 gdk_font_unref(new_style
->font
);
3114 new_style
->font
= font
;
3118 gtk_widget_set_style(text
, new_style
);
3120 color
[0] = quote_color
;
3121 cmap
= gdk_window_get_colormap(window
->window
);
3122 gdk_colormap_alloc_colors(cmap
, color
, 1, FALSE
, TRUE
, success
);
3123 if (success
[0] == FALSE
) {
3124 g_warning("Compose: color allocation failed.\n");
3125 style
= gtk_widget_get_style(text
);
3126 quote_color
= style
->black
;
3129 n_entries
= sizeof(compose_popup_entries
) /
3130 sizeof(compose_popup_entries
[0]);
3131 popupmenu
= menu_create_items(compose_popup_entries
, n_entries
,
3132 "<Compose>", &popupfactory
,
3135 ifactory
= gtk_item_factory_from_widget(menubar
);
3136 menu_set_sensitive(ifactory
, "/Edit/Undo", FALSE
);
3137 menu_set_sensitive(ifactory
, "/Edit/Redo", FALSE
);
3140 if (account->protocol == A_NNTP) {
3141 gtk_widget_hide(to_hbox);
3142 gtk_widget_hide(to_entry);
3143 gtk_widget_hide(cc_hbox);
3144 gtk_widget_hide(cc_entry);
3145 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
3146 gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
3148 gtk_widget_hide(newsgroups_hbox);
3149 gtk_widget_hide(newsgroups_entry);
3150 gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
3152 menu_set_sensitive(ifactory, "/Message/Followup to", FALSE);
3156 switch (prefs_common
.toolbar_style
) {
3158 gtk_widget_hide(handlebox
);
3161 gtk_toolbar_set_style(GTK_TOOLBAR(compose
->toolbar
),
3165 gtk_toolbar_set_style(GTK_TOOLBAR(compose
->toolbar
),
3169 gtk_toolbar_set_style(GTK_TOOLBAR(compose
->toolbar
),
3174 gtk_widget_show(window
);
3176 address_completion_start(window
);
3178 compose
->window
= window
;
3179 compose
->vbox
= vbox
;
3180 compose
->menubar
= menubar
;
3181 compose
->handlebox
= handlebox
;
3183 compose
->vbox2
= vbox2
;
3185 compose
->table_vbox
= table_vbox
;
3186 compose
->table
= table
;
3187 compose
->to_hbox
= to_hbox
;
3188 compose
->to_entry
= to_entry
;
3189 compose
->newsgroups_hbox
= newsgroups_hbox
;
3190 compose
->newsgroups_entry
= newsgroups_entry
;
3191 compose
->subject_entry
= subject_entry
;
3192 compose
->cc_hbox
= cc_hbox
;
3193 compose
->cc_entry
= cc_entry
;
3194 compose
->bcc_hbox
= bcc_hbox
;
3195 compose
->bcc_entry
= bcc_entry
;
3196 compose
->reply_hbox
= reply_hbox
;
3197 compose
->reply_entry
= reply_entry
;
3198 compose
->followup_hbox
= followup_hbox
;
3199 compose
->followup_entry
= followup_entry
;
3201 compose
->paned
= paned
;
3203 compose
->attach_scrwin
= attach_scrwin
;
3204 compose
->attach_clist
= attach_clist
;
3206 compose
->edit_vbox
= edit_vbox
;
3207 compose
->ruler_hbox
= ruler_hbox
;
3208 compose
->ruler
= ruler
;
3209 compose
->scrolledwin
= scrolledwin
;
3210 compose
->text
= text
;
3212 compose
->focused_editable
= NULL
;
3214 compose
->popupmenu
= popupmenu
;
3215 compose
->popupfactory
= popupfactory
;
3217 compose
->mode
= COMPOSE_NEW
;
3219 compose
->replyto
= NULL
;
3221 compose
->bcc
= NULL
;
3222 compose
->followup_to
= NULL
;
3223 compose
->inreplyto
= NULL
;
3224 compose
->references
= NULL
;
3225 compose
->msgid
= NULL
;
3226 compose
->boundary
= NULL
;
3229 compose
->use_signing
= FALSE
;
3230 compose
->use_encryption
= FALSE
;
3231 #endif /* USE_GPGME */
3233 compose
->modified
= FALSE
;
3235 compose
->return_receipt
= FALSE
;
3237 compose
->to_list
= NULL
;
3238 compose
->newsgroup_list
= NULL
;
3240 compose
->exteditor_file
= NULL
;
3241 compose
->exteditor_pid
= -1;
3242 compose
->exteditor_readdes
= -1;
3243 compose
->exteditor_tag
= -1;
3245 compose_set_title(compose
);
3247 compose
->use_bcc
= FALSE
;
3248 compose
->use_replyto
= FALSE
;
3249 compose
->use_followupto
= FALSE
;
3252 if (account->protocol != A_NNTP) {
3253 menuitem = gtk_item_factory_get_item(ifactory, "/Message/To");
3254 gtk_check_menu_item_set_active
3255 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3256 gtk_widget_set_sensitive(menuitem, FALSE);
3257 menuitem = gtk_item_factory_get_item(ifactory, "/Message/Cc");
3258 gtk_check_menu_item_set_active
3259 (GTK_CHECK_MENU_ITEM(menuitem), TRUE);
3260 gtk_widget_set_sensitive(menuitem, FALSE);
3263 if (account
->set_autocc
&& account
->auto_cc
) {
3264 compose
->use_cc
= TRUE
;
3265 gtk_entry_set_text(GTK_ENTRY(cc_entry
), account
->auto_cc
);
3266 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Cc");
3267 gtk_check_menu_item_set_active
3268 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
3271 if (account
->set_autobcc
) {
3272 compose
->use_bcc
= TRUE
;
3273 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Bcc");
3274 gtk_check_menu_item_set_active
3275 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
3276 if (account
->auto_bcc
)
3277 gtk_entry_set_text(GTK_ENTRY(bcc_entry
),
3280 if (account
->set_autoreplyto
) {
3281 compose
->use_replyto
= TRUE
;
3282 menuitem
= gtk_item_factory_get_item(ifactory
,
3283 "/Message/Reply to");
3284 gtk_check_menu_item_set_active
3285 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
3286 if (account
->auto_replyto
)
3287 gtk_entry_set_text(GTK_ENTRY(reply_entry
),
3288 account
->auto_replyto
);
3291 menuitem
= gtk_item_factory_get_item(ifactory
, "/Tool/Show ruler");
3292 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
3293 prefs_common
.show_ruler
);
3296 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Sign");
3297 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
3298 prefs_common
.default_sign
);
3299 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Encrypt");
3300 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
3301 prefs_common
.default_encrypt
);
3302 #endif /* USE_GPGME */
3304 addressbook_set_target_compose(compose
);
3306 compose_list
= g_list_append(compose_list
, compose
);
3309 compose->use_to = FALSE;
3310 compose->use_cc = FALSE;
3312 compose
->use_attach
= FALSE
;
3314 if (!compose
->use_bcc
) {
3315 gtk_widget_hide(bcc_hbox
);
3316 gtk_widget_hide(bcc_entry
);
3317 gtk_table_set_row_spacing(GTK_TABLE(table
), 4, 0);
3319 if (!compose
->use_replyto
) {
3320 gtk_widget_hide(reply_hbox
);
3321 gtk_widget_hide(reply_entry
);
3322 gtk_table_set_row_spacing(GTK_TABLE(table
), 5, 0);
3324 if (!compose
->use_followupto
) {
3325 gtk_widget_hide(followup_hbox
);
3326 gtk_widget_hide(followup_entry
);
3327 gtk_table_set_row_spacing(GTK_TABLE(table
), 6, 0);
3330 if (!prefs_common
.show_ruler
)
3331 gtk_widget_hide(ruler_hbox
);
3333 select_account(compose
, account
);
3338 #include "pixmaps/stock_mail_send.xpm"
3339 #include "pixmaps/stock_mail_send_queue.xpm"
3340 #include "pixmaps/stock_mail.xpm"
3341 #include "pixmaps/stock_paste.xpm"
3342 #include "pixmaps/stock_mail_attach.xpm"
3343 #include "pixmaps/stock_mail_compose.xpm"
3344 #include "pixmaps/linewrap.xpm"
3345 #include "pixmaps/tb_address_book.xpm"
3347 #define CREATE_TOOLBAR_ICON(xpm_d) \
3349 icon = gdk_pixmap_create_from_xpm_d(container->window, &mask, \
3350 &container->style->white, \
3352 icon_wid = gtk_pixmap_new(icon, mask); \
3355 static void compose_toolbar_create(Compose
*compose
, GtkWidget
*container
)
3360 GtkWidget
*icon_wid
;
3361 GtkWidget
*send_btn
;
3362 GtkWidget
*sendl_btn
;
3363 GtkWidget
*draft_btn
;
3364 GtkWidget
*insert_btn
;
3365 GtkWidget
*attach_btn
;
3367 GtkWidget
*exteditor_btn
;
3368 GtkWidget
*linewrap_btn
;
3369 GtkWidget
*addrbook_btn
;
3371 toolbar
= gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL
,
3373 gtk_container_add(GTK_CONTAINER(container
), toolbar
);
3374 gtk_container_set_border_width(GTK_CONTAINER(container
), 2);
3375 gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar
), GTK_RELIEF_NONE
);
3376 gtk_toolbar_set_space_style(GTK_TOOLBAR(toolbar
),
3377 GTK_TOOLBAR_SPACE_LINE
);
3379 CREATE_TOOLBAR_ICON(stock_mail_send_xpm
);
3380 send_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3384 icon_wid
, toolbar_send_cb
, compose
);
3386 CREATE_TOOLBAR_ICON(stock_mail_send_queue_xpm
);
3387 /* CREATE_TOOLBAR_ICON(tb_mail_queue_send_xpm); */
3388 sendl_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3390 _("Put into queue folder and send later"),
3392 icon_wid
, toolbar_send_later_cb
,
3395 CREATE_TOOLBAR_ICON(stock_mail_xpm
);
3396 draft_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3398 _("Save to draft folder"),
3400 icon_wid
, toolbar_draft_cb
,
3403 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar
));
3405 CREATE_TOOLBAR_ICON(stock_paste_xpm
);
3406 insert_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3410 icon_wid
, toolbar_insert_cb
,
3413 CREATE_TOOLBAR_ICON(stock_mail_attach_xpm
);
3414 attach_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3418 icon_wid
, toolbar_attach_cb
,
3421 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar
));
3423 CREATE_TOOLBAR_ICON(stock_mail_xpm
);
3424 sig_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3426 _("Insert signature"),
3428 icon_wid
, toolbar_sig_cb
, compose
);
3430 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar
));
3432 CREATE_TOOLBAR_ICON(stock_mail_compose_xpm
);
3433 exteditor_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3435 _("Edit with external editor"),
3438 toolbar_ext_editor_cb
,
3441 CREATE_TOOLBAR_ICON(linewrap_xpm
);
3442 linewrap_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3444 _("Wrap long lines"),
3447 toolbar_linewrap_cb
,
3450 gtk_toolbar_append_space(GTK_TOOLBAR(toolbar
));
3452 CREATE_TOOLBAR_ICON(tb_address_book_xpm
);
3453 addrbook_btn
= gtk_toolbar_append_item(GTK_TOOLBAR(toolbar
),
3457 icon_wid
, toolbar_address_cb
,
3460 compose
->toolbar
= toolbar
;
3461 compose
->send_btn
= send_btn
;
3462 compose
->sendl_btn
= sendl_btn
;
3463 compose
->draft_btn
= draft_btn
;
3464 compose
->insert_btn
= insert_btn
;
3465 compose
->attach_btn
= attach_btn
;
3466 compose
->sig_btn
= sig_btn
;
3467 compose
->exteditor_btn
= exteditor_btn
;
3468 compose
->linewrap_btn
= linewrap_btn
;
3469 compose
->addrbook_btn
= addrbook_btn
;
3471 gtk_widget_show_all(toolbar
);
3474 #undef CREATE_TOOLBAR_ICON
3476 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
3482 gint num
= 0, def_menu
= 0;
3484 accounts
= account_get_list();
3485 g_return_val_if_fail(accounts
!= NULL
, NULL
);
3487 hbox
= gtk_hbox_new(FALSE
, 0);
3488 optmenu
= gtk_option_menu_new();
3489 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, FALSE
, FALSE
, 0);
3490 menu
= gtk_menu_new();
3492 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
3493 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
3494 GtkWidget
*menuitem
;
3497 if (ac
== compose
->account
) def_menu
= num
;
3499 name
= g_strdup_printf("%s: %s <%s>",
3500 ac
->account_name
, ac
->name
, ac
->address
);
3501 MENUITEM_ADD(menu
, menuitem
, name
, ac
);
3503 gtk_signal_connect(GTK_OBJECT(menuitem
), "activate",
3504 GTK_SIGNAL_FUNC(account_activated
),
3508 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), menu
);
3509 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu
), def_menu
);
3514 static void compose_destroy(Compose
*compose
)
3517 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
3520 /* NOTE: address_completion_end() does nothing with the window
3521 * however this may change. */
3522 address_completion_end(compose
->window
);
3524 slist_free_strings(compose
->to_list
);
3525 g_slist_free(compose
->to_list
);
3526 slist_free_strings(compose
->newsgroup_list
);
3527 g_slist_free(compose
->newsgroup_list
);
3529 procmsg_msginfo_free(compose
->targetinfo
);
3531 g_free(compose
->replyto
);
3532 g_free(compose
->cc
);
3533 g_free(compose
->bcc
);
3534 g_free(compose
->newsgroups
);
3535 g_free(compose
->followup_to
);
3537 g_free(compose
->inreplyto
);
3538 g_free(compose
->references
);
3539 g_free(compose
->msgid
);
3540 g_free(compose
->boundary
);
3542 g_free(compose
->exteditor_file
);
3544 for (row
= 0; (ainfo
= gtk_clist_get_row_data(clist
, row
)) != NULL
;
3546 compose_attach_info_free(ainfo
);
3548 if (addressbook_get_target_compose() == compose
)
3549 addressbook_set_target_compose(NULL
);
3551 prefs_common
.compose_width
= compose
->scrolledwin
->allocation
.width
;
3552 prefs_common
.compose_height
= compose
->window
->allocation
.height
;
3554 gtk_widget_destroy(compose
->paned
);
3558 compose_list
= g_list_remove(compose_list
, compose
);
3561 static void compose_attach_info_free(AttachInfo
*ainfo
)
3563 g_free(ainfo
->file
);
3564 g_free(ainfo
->content_type
);
3565 g_free(ainfo
->name
);
3569 static void compose_attach_remove_selected(Compose
*compose
)
3571 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
3575 while (clist
->selection
!= NULL
) {
3576 row
= GPOINTER_TO_INT(clist
->selection
->data
);
3577 ainfo
= gtk_clist_get_row_data(clist
, row
);
3578 compose_attach_info_free(ainfo
);
3579 gtk_clist_remove(clist
, row
);
3583 static struct _AttachProperty
3586 GtkWidget
*mimetype_entry
;
3587 GtkWidget
*encoding_optmenu
;
3588 GtkWidget
*path_entry
;
3589 GtkWidget
*filename_entry
;
3591 GtkWidget
*cancel_btn
;
3594 static void compose_attach_property(Compose
*compose
)
3596 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
3600 GtkOptionMenu
*optmenu
;
3602 if (!clist
->selection
) return;
3603 row
= GPOINTER_TO_INT(clist
->selection
->data
);
3605 ainfo
= gtk_clist_get_row_data(clist
, row
);
3608 if (!attach_prop
.window
)
3609 compose_attach_property_create(&cancelled
);
3610 gtk_widget_grab_focus(attach_prop
.ok_btn
);
3611 gtk_widget_show(attach_prop
.window
);
3612 manage_window_set_transient(GTK_WINDOW(attach_prop
.window
));
3614 optmenu
= GTK_OPTION_MENU(attach_prop
.encoding_optmenu
);
3615 if (ainfo
->encoding
== ENC_UNKNOWN
)
3616 gtk_option_menu_set_history(optmenu
, ENC_BASE64
);
3618 gtk_option_menu_set_history(optmenu
, ainfo
->encoding
);
3620 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
3621 ainfo
->content_type
? ainfo
->content_type
: "");
3622 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
3623 ainfo
->file
? ainfo
->file
: "");
3624 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
3625 ainfo
->name
? ainfo
->name
: "");
3629 gchar
*cnttype
= NULL
;
3633 GtkWidget
*menuitem
;
3637 if (cancelled
== TRUE
) {
3638 gtk_widget_hide(attach_prop
.window
);
3642 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
3643 if (*text
!= '\0') {
3646 text
= g_strstrip(g_strdup(text
));
3647 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
3648 cnttype
= g_strdup(text
);
3651 alertpanel_error(_("Invalid MIME type."));
3657 menu
= gtk_option_menu_get_menu(optmenu
);
3658 menuitem
= gtk_menu_get_active(GTK_MENU(menu
));
3659 ainfo
->encoding
= GPOINTER_TO_INT
3660 (gtk_object_get_user_data(GTK_OBJECT(menuitem
)));
3662 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
3663 if (*text
!= '\0') {
3664 if (is_file_exist(text
) &&
3665 (size
= get_file_size(text
)) > 0)
3666 file
= g_strdup(text
);
3669 (_("File doesn't exist or is empty."));
3675 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
3676 if (*text
!= '\0') {
3677 g_free(ainfo
->name
);
3678 ainfo
->name
= g_strdup(text
);
3682 g_free(ainfo
->content_type
);
3683 ainfo
->content_type
= cnttype
;
3686 g_free(ainfo
->file
);
3692 gtk_clist_set_text(clist
, row
, COL_MIMETYPE
,
3693 ainfo
->content_type
);
3694 gtk_clist_set_text(clist
, row
, COL_SIZE
,
3695 to_human_readable(ainfo
->size
));
3696 gtk_clist_set_text(clist
, row
, COL_NAME
, ainfo
->name
);
3698 gtk_widget_hide(attach_prop
.window
);
3703 #define SET_LABEL_AND_ENTRY(str, entry, top) \
3705 label = gtk_label_new(str); \
3706 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
3707 GTK_FILL, 0, 0, 0); \
3708 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
3710 entry = gtk_entry_new(); \
3711 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
3712 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
3715 static void compose_attach_property_create(gboolean
*cancelled
)
3721 GtkWidget
*mimetype_entry
;
3724 GtkWidget
*optmenu_menu
;
3725 GtkWidget
*menuitem
;
3726 GtkWidget
*path_entry
;
3727 GtkWidget
*filename_entry
;
3730 GtkWidget
*cancel_btn
;
3731 GList
*mime_type_list
, *strlist
;
3733 debug_print("Creating attach_property window...\n");
3735 window
= gtk_window_new(GTK_WINDOW_DIALOG
);
3736 gtk_widget_set_usize(window
, 480, -1);
3737 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
3738 gtk_window_set_title(GTK_WINDOW(window
), _("Property"));
3739 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
3740 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
3741 gtk_signal_connect(GTK_OBJECT(window
), "delete_event",
3742 GTK_SIGNAL_FUNC(attach_property_delete_event
),
3744 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
3745 GTK_SIGNAL_FUNC(attach_property_key_pressed
),
3748 vbox
= gtk_vbox_new(FALSE
, 8);
3749 gtk_container_add(GTK_CONTAINER(window
), vbox
);
3751 table
= gtk_table_new(4, 2, FALSE
);
3752 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
3753 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
3754 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
3756 label
= gtk_label_new(_("MIME type"));
3757 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
3759 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
3760 mimetype_entry
= gtk_combo_new();
3761 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
3762 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
3764 /* stuff with list */
3765 mime_type_list
= procmime_get_mime_type_list();
3767 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
3768 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
3769 strlist
= g_list_append(strlist
,
3770 g_strdup_printf("%s/%s",
3771 type
->type
, type
->sub_type
));
3774 gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry
), strlist
);
3776 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
3777 mime_type_list
= mime_type_list
->next
)
3778 g_free(mime_type_list
->data
);
3779 g_list_free(strlist
);
3781 mimetype_entry
= GTK_COMBO(mimetype_entry
)->entry
;
3783 label
= gtk_label_new(_("Encoding"));
3784 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
3786 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
3788 hbox
= gtk_hbox_new(FALSE
, 0);
3789 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
3790 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
3792 optmenu
= gtk_option_menu_new();
3793 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, FALSE
, FALSE
, 0);
3795 optmenu_menu
= gtk_menu_new();
3796 MENUITEM_ADD(optmenu_menu
, menuitem
, "7bit", ENC_7BIT
);
3797 gtk_widget_set_sensitive(menuitem
, FALSE
);
3798 MENUITEM_ADD(optmenu_menu
, menuitem
, "8bit", ENC_8BIT
);
3799 gtk_widget_set_sensitive(menuitem
, FALSE
);
3800 MENUITEM_ADD(optmenu_menu
, menuitem
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
3801 gtk_widget_set_sensitive(menuitem
, FALSE
);
3802 MENUITEM_ADD(optmenu_menu
, menuitem
, "base64", ENC_BASE64
);
3804 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), optmenu_menu
);
3806 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
3807 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
3809 gtkut_button_set_create(&hbbox
, &ok_btn
, _("OK"),
3810 &cancel_btn
, _("Cancel"), NULL
, NULL
);
3811 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
3812 gtk_widget_grab_default(ok_btn
);
3814 gtk_signal_connect(GTK_OBJECT(ok_btn
), "clicked",
3815 GTK_SIGNAL_FUNC(attach_property_ok
),
3817 gtk_signal_connect(GTK_OBJECT(cancel_btn
), "clicked",
3818 GTK_SIGNAL_FUNC(attach_property_cancel
),
3821 gtk_widget_show_all(vbox
);
3823 attach_prop
.window
= window
;
3824 attach_prop
.mimetype_entry
= mimetype_entry
;
3825 attach_prop
.encoding_optmenu
= optmenu
;
3826 attach_prop
.path_entry
= path_entry
;
3827 attach_prop
.filename_entry
= filename_entry
;
3828 attach_prop
.ok_btn
= ok_btn
;
3829 attach_prop
.cancel_btn
= cancel_btn
;
3832 #undef SET_LABEL_AND_ENTRY
3834 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
3840 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
3846 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
3847 gboolean
*cancelled
)
3855 static void attach_property_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
3856 gboolean
*cancelled
)
3858 if (event
&& event
->keyval
== GDK_Escape
) {
3864 static void compose_exec_ext_editor(Compose
*compose
)
3870 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg.%08x",
3871 g_get_tmp_dir(), G_DIR_SEPARATOR
, (gint
)compose
);
3873 if (pipe(pipe_fds
) < 0) {
3878 if ((pid
= fork()) < 0) {
3884 /* close the write side of the pipe */
3887 compose
->exteditor_file
= g_strdup(tmp
);
3888 compose
->exteditor_pid
= pid
;
3889 compose
->exteditor_readdes
= pipe_fds
[0];
3891 compose_set_ext_editor_sensitive(compose
, FALSE
);
3893 compose
->exteditor_tag
=
3894 gdk_input_add(pipe_fds
[0], GDK_INPUT_READ
,
3895 compose_input_cb
, compose
);
3896 } else { /* process-monitoring process */
3902 /* close the read side of the pipe */
3905 if (compose_write_body_to_file(compose
, tmp
) < 0) {
3906 fd_write(pipe_fds
[1], "2\n", 2);
3910 pid_ed
= compose_exec_ext_editor_real(tmp
);
3912 fd_write(pipe_fds
[1], "1\n", 2);
3916 /* wait until editor is terminated */
3917 waitpid(pid_ed
, NULL
, 0);
3919 fd_write(pipe_fds
[1], "0\n", 2);
3926 static gint
compose_exec_ext_editor_real(const gchar
*file
)
3928 static gchar
*def_cmd
= "emacs %s";
3934 g_return_val_if_fail(file
!= NULL
, -1);
3936 if ((pid
= fork()) < 0) {
3941 if (pid
!= 0) return pid
;
3943 /* grandchild process */
3945 if (setpgid(0, getppid()))
3948 if (prefs_common
.ext_editor_cmd
&&
3949 (p
= strchr(prefs_common
.ext_editor_cmd
, '%')) &&
3950 *(p
+ 1) == 's' && !strchr(p
+ 2, '%')) {
3951 g_snprintf(buf
, sizeof(buf
), prefs_common
.ext_editor_cmd
, file
);
3953 if (prefs_common
.ext_editor_cmd
)
3954 g_warning(_("External editor command line is invalid: `%s'\n"),
3955 prefs_common
.ext_editor_cmd
);
3956 g_snprintf(buf
, sizeof(buf
), def_cmd
, file
);
3959 cmdline
= g_strsplit(buf
, " ", 1024);
3960 execvp(cmdline
[0], cmdline
);
3963 g_strfreev(cmdline
);
3968 static gboolean
compose_ext_editor_kill(Compose
*compose
)
3970 pid_t pgid
= compose
->exteditor_pid
* -1;
3973 ret
= kill(pgid
, 0);
3975 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
3979 msg
= g_strdup_printf
3980 (_("The external editor is still working.\n"
3981 "Force terminating the process?\n"
3982 "process group id: %d"), -pgid
);
3983 val
= alertpanel(_("Notice"), msg
, _("Yes"), _("+No"), NULL
);
3986 if (val
== G_ALERTDEFAULT
) {
3987 gdk_input_remove(compose
->exteditor_tag
);
3988 close(compose
->exteditor_readdes
);
3990 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
3991 waitpid(compose
->exteditor_pid
, NULL
, 0);
3993 g_warning(_("Terminated process group id: %d"), -pgid
);
3994 g_warning(_("Temporary file: %s"),
3995 compose
->exteditor_file
);
3997 compose_set_ext_editor_sensitive(compose
, TRUE
);
3999 g_free(compose
->exteditor_file
);
4000 compose
->exteditor_file
= NULL
;
4001 compose
->exteditor_pid
= -1;
4002 compose
->exteditor_readdes
= -1;
4003 compose
->exteditor_tag
= -1;
4011 static void compose_input_cb(gpointer data
, gint source
,
4012 GdkInputCondition condition
)
4015 Compose
*compose
= (Compose
*)data
;
4018 debug_print(_("Compose: input from monitoring process\n"));
4020 gdk_input_remove(compose
->exteditor_tag
);
4023 if (read(source
, &buf
[i
], 1) < 1) {
4027 if (buf
[i
] == '\n') {
4032 if (i
== sizeof(buf
) - 1)
4036 waitpid(compose
->exteditor_pid
, NULL
, 0);
4038 if (buf
[0] == '0') { /* success */
4039 GtkSText
*text
= GTK_STEXT(compose
->text
);
4041 gtk_stext_freeze(text
);
4042 gtk_stext_set_point(text
, 0);
4043 gtk_stext_forward_delete(text
, gtk_stext_get_length(text
));
4044 compose_insert_file(compose
, compose
->exteditor_file
);
4045 compose_changed_cb(NULL
, compose
);
4046 gtk_stext_thaw(text
);
4048 if (unlink(compose
->exteditor_file
) < 0)
4049 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
4050 } else if (buf
[0] == '1') { /* failed */
4051 g_warning(_("Couldn't exec external editor\n"));
4052 if (unlink(compose
->exteditor_file
) < 0)
4053 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
4054 } else if (buf
[0] == '2') {
4055 g_warning(_("Couldn't write to file\n"));
4056 } else if (buf
[0] == '3') {
4057 g_warning(_("Pipe read failed\n"));
4062 compose_set_ext_editor_sensitive(compose
, TRUE
);
4064 g_free(compose
->exteditor_file
);
4065 compose
->exteditor_file
= NULL
;
4066 compose
->exteditor_pid
= -1;
4067 compose
->exteditor_readdes
= -1;
4068 compose
->exteditor_tag
= -1;
4071 static void compose_set_ext_editor_sensitive(Compose
*compose
,
4074 GtkItemFactory
*ifactory
;
4076 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
4078 menu_set_sensitive(ifactory
, "/Message/Send", sensitive
);
4079 menu_set_sensitive(ifactory
, "/Message/Send later", sensitive
);
4080 menu_set_sensitive(ifactory
, "/Message/Save to draft folder",
4082 menu_set_sensitive(ifactory
, "/File/Insert file", sensitive
);
4083 menu_set_sensitive(ifactory
, "/File/Insert signature", sensitive
);
4084 menu_set_sensitive(ifactory
, "/Edit/Wrap long lines", sensitive
);
4085 menu_set_sensitive(ifactory
, "/Edit/Edit with external editor",
4088 gtk_widget_set_sensitive(compose
->text
, sensitive
);
4089 gtk_widget_set_sensitive(compose
->send_btn
, sensitive
);
4090 gtk_widget_set_sensitive(compose
->sendl_btn
, sensitive
);
4091 gtk_widget_set_sensitive(compose
->draft_btn
, sensitive
);
4092 gtk_widget_set_sensitive(compose
->insert_btn
, sensitive
);
4093 gtk_widget_set_sensitive(compose
->sig_btn
, sensitive
);
4094 gtk_widget_set_sensitive(compose
->exteditor_btn
, sensitive
);
4095 gtk_widget_set_sensitive(compose
->linewrap_btn
, sensitive
);
4098 static gint
calc_cursor_xpos(GtkSText
*text
, gint extra
, gint char_width
)
4102 cursor_pos
= (text
->cursor_pos_x
- extra
) / char_width
;
4103 cursor_pos
= MAX(cursor_pos
, 0);
4108 /* callback functions */
4110 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
4111 * includes "non-client" (windows-izm) in calculation, so this calculation
4112 * may not be accurate.
4114 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
4115 GtkAllocation
*allocation
,
4116 GtkSHRuler
*shruler
)
4118 if (prefs_common
.show_ruler
) {
4120 gint line_width_in_chars
;
4122 char_width
= gtkut_get_font_width
4123 (GTK_WIDGET(widget
)->style
->font
);
4124 line_width_in_chars
=
4125 (allocation
->width
- allocation
->x
) / char_width
;
4127 /* got the maximum */
4128 gtk_ruler_set_range(GTK_RULER(shruler
),
4129 0.0, line_width_in_chars
,
4130 calc_cursor_xpos(GTK_STEXT(widget
),
4133 /*line_width_in_chars*/ char_width
);
4139 static void toolbar_send_cb(GtkWidget
*widget
, gpointer data
)
4141 compose_send_cb(data
, 0, NULL
);
4144 static void toolbar_send_later_cb(GtkWidget
*widget
, gpointer data
)
4146 compose_send_later_cb(data
, 0, NULL
);
4149 static void toolbar_draft_cb(GtkWidget
*widget
, gpointer data
)
4151 compose_draft_cb(data
, 0, NULL
);
4154 static void toolbar_insert_cb(GtkWidget
*widget
, gpointer data
)
4156 compose_insert_file_cb(data
, 0, NULL
);
4159 static void toolbar_attach_cb(GtkWidget
*widget
, gpointer data
)
4161 compose_attach_cb(data
, 0, NULL
);
4164 static void toolbar_sig_cb(GtkWidget
*widget
, gpointer data
)
4166 Compose
*compose
= (Compose
*)data
;
4168 compose_insert_sig(compose
);
4171 static void toolbar_ext_editor_cb(GtkWidget
*widget
, gpointer data
)
4173 Compose
*compose
= (Compose
*)data
;
4175 compose_exec_ext_editor(compose
);
4178 static void toolbar_linewrap_cb(GtkWidget
*widget
, gpointer data
)
4180 Compose
*compose
= (Compose
*)data
;
4182 compose_wrap_line(compose
);
4185 static void toolbar_address_cb(GtkWidget
*widget
, gpointer data
)
4187 compose_address_cb(data
, 0, NULL
);
4190 static void select_account(Compose
* compose
, PrefsAccount
* ac
)
4192 compose
->account
= ac
;
4193 compose_set_title(compose
);
4195 if (ac
->protocol
== A_NNTP
) {
4196 GtkItemFactory
*ifactory
;
4197 GtkWidget
*menuitem
;
4199 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
4200 menu_set_sensitive(ifactory
,
4201 "/Message/Followup to", TRUE
);
4203 gtk_widget_show(compose
->newsgroups_hbox
);
4204 gtk_widget_show(compose
->newsgroups_entry
);
4205 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4208 compose
->use_to
= FALSE
;
4209 compose
->use_cc
= FALSE
;
4211 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/To");
4212 gtk_check_menu_item_set_active
4213 (GTK_CHECK_MENU_ITEM(menuitem
), FALSE
);
4214 menu_set_sensitive(ifactory
,
4215 "/Message/To", TRUE
);
4216 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Cc");
4217 gtk_check_menu_item_set_active
4218 (GTK_CHECK_MENU_ITEM(menuitem
), FALSE
);
4220 gtk_widget_hide(compose
->to_hbox
);
4221 gtk_widget_hide(compose
->to_entry
);
4222 gtk_widget_hide(compose
->cc_hbox
);
4223 gtk_widget_hide(compose
->cc_entry
);
4225 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4227 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4231 GtkItemFactory
*ifactory
;
4232 GtkWidget
*menuitem
;
4234 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
4235 menu_set_sensitive(ifactory
,
4236 "/Message/Followup to", FALSE
);
4238 gtk_entry_set_text(GTK_ENTRY(compose
->newsgroups_entry
), "");
4239 gtk_widget_hide(compose
->newsgroups_hbox
);
4240 gtk_widget_hide(compose
->newsgroups_entry
);
4241 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4244 compose
->use_to
= TRUE
;
4245 compose
->use_cc
= TRUE
;
4247 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/To");
4248 gtk_check_menu_item_set_active
4249 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
4250 menu_set_sensitive(ifactory
,
4251 "/Message/To", FALSE
);
4252 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Cc");
4253 gtk_check_menu_item_set_active
4254 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
4256 gtk_widget_show(compose
->to_hbox
);
4257 gtk_widget_show(compose
->to_entry
);
4258 gtk_widget_show(compose
->cc_hbox
);
4259 gtk_widget_show(compose
->cc_entry
);
4261 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4263 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
),
4266 gtk_widget_queue_resize(compose
->table_vbox
);
4269 static void account_activated(GtkMenuItem
*menuitem
, gpointer data
)
4271 Compose
*compose
= (Compose
*)data
;
4275 ac
= (PrefsAccount
*)gtk_object_get_user_data(GTK_OBJECT(menuitem
));
4276 g_return_if_fail(ac
!= NULL
);
4278 if (ac
!= compose
->account
)
4279 select_account(compose
, ac
);
4282 static void attach_selected(GtkCList
*clist
, gint row
, gint column
,
4283 GdkEvent
*event
, gpointer data
)
4285 Compose
*compose
= (Compose
*)data
;
4287 if (event
&& event
->type
== GDK_2BUTTON_PRESS
)
4288 compose_attach_property(compose
);
4291 static void attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
4294 Compose
*compose
= (Compose
*)data
;
4295 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
4300 if (event
->button
== 3) {
4301 if ((clist
->selection
&& !clist
->selection
->next
) ||
4302 !clist
->selection
) {
4303 gtk_clist_unselect_all(clist
);
4304 if (gtk_clist_get_selection_info(clist
,
4307 gtk_clist_select_row(clist
, row
, column
);
4308 gtkut_clist_set_focus_row(clist
, row
);
4311 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
4312 NULL
, NULL
, event
->button
, event
->time
);
4316 static void attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
4319 Compose
*compose
= (Compose
*)data
;
4323 switch (event
->keyval
) {
4325 compose_attach_remove_selected(compose
);
4330 static void compose_send_cb(gpointer data
, guint action
, GtkWidget
*widget
)
4332 Compose
*compose
= (Compose
*)data
;
4335 val
= compose_send(compose
);
4337 if (val
== 0) gtk_widget_destroy(compose
->window
);
4340 static void compose_send_later_cb(gpointer data
, guint action
,
4343 Compose
*compose
= (Compose
*)data
;
4345 gchar
*to
, *newsgroups
;
4347 to
= gtk_entry_get_text(GTK_ENTRY(compose
->to_entry
));
4348 newsgroups
= gtk_entry_get_text(GTK_ENTRY(compose
->newsgroups_entry
));
4349 if (*to
== '\0' && *newsgroups
== '\0') {
4350 alertpanel_error(_("Recipient is not specified."));
4354 g_snprintf(tmp
, 22, "%s%ctmpmsg%d",
4355 g_get_tmp_dir(), G_DIR_SEPARATOR
, (gint
)compose
);
4357 if (prefs_common
.linewrap_at_send
)
4358 compose_wrap_line(compose
);
4360 if (compose_write_to_file(compose
, tmp
, FALSE
) < 0 ||
4361 compose_queue(compose
, tmp
) < 0) {
4362 alertpanel_error(_("Can't queue the message."));
4366 if (prefs_common
.savemsg
) {
4367 if (compose_save_to_outbox(compose
, tmp
) < 0)
4369 (_("Can't save the message to outbox."));
4372 if (unlink(tmp
) < 0)
4373 FILE_OP_ERROR(tmp
, "unlink");
4375 gtk_widget_destroy(compose
->window
);
4378 static void compose_draft_cb(gpointer data
, guint action
, GtkWidget
*widget
)
4380 Compose
*compose
= (Compose
*)data
;
4384 draft
= folder_get_default_draft();
4385 folder_item_scan(draft
);
4387 if (procmsg_msg_exist(compose
->targetinfo
) &&
4388 compose
->targetinfo
->folder
== draft
) {
4389 if (folder_item_remove_msg(draft
,
4390 compose
->targetinfo
->msgnum
) < 0)
4391 g_warning(_("can't remove the old draft message\n"));
4393 tmp
= g_strdup_printf("%s%cdraft.%d", g_get_tmp_dir(),
4394 G_DIR_SEPARATOR
, (gint
)compose
);
4396 if (compose_write_to_file(compose
, tmp
, TRUE
) < 0) {
4401 if (folder_item_add_msg(draft
, tmp
, TRUE
) < 0) {
4409 /* folderview_scan_folder_a(DRAFT_DIR, TRUE); */
4411 gtk_widget_destroy(compose
->window
);
4414 static void compose_attach_cb(gpointer data
, guint action
, GtkWidget
*widget
)
4416 Compose
*compose
= (Compose
*)data
;
4419 file_list
= filesel_select_multiple_files(_("Select file"), NULL
);
4424 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
4425 gchar
*file
= (gchar
*) tmp
->data
;
4426 compose_attach_append(compose
, file
, MIME_UNKNOWN
);
4429 g_list_free(file_list
);
4433 static void compose_insert_file_cb(gpointer data
, guint action
,
4436 Compose
*compose
= (Compose
*)data
;
4439 file_list
= filesel_select_multiple_files(_("Select file"), NULL
);
4444 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
4445 gchar
*file
= (gchar
*) tmp
->data
;
4446 compose_insert_file(compose
, file
);
4449 g_list_free(file_list
);
4453 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
4456 compose_close_cb(data
, 0, NULL
);
4460 static void compose_close_cb(gpointer data
, guint action
, GtkWidget
*widget
)
4462 Compose
*compose
= (Compose
*)data
;
4465 if (compose
->exteditor_tag
!= -1) {
4466 if (!compose_ext_editor_kill(compose
))
4470 if (compose
->modified
) {
4471 val
= alertpanel(_("Discard message"),
4472 _("This message has been modified. discard it?"),
4473 _("Discard"), _("to Draft"), _("Cancel"));
4476 case G_ALERTDEFAULT
:
4478 case G_ALERTALTERNATE
:
4479 compose_draft_cb(data
, 0, NULL
);
4486 gtk_widget_destroy(compose
->window
);
4489 static void compose_address_cb(gpointer data
, guint action
, GtkWidget
*widget
)
4491 Compose
*compose
= (Compose
*)data
;
4493 addressbook_open(compose
);
4496 static void compose_ext_editor_cb(gpointer data
, guint action
,
4499 Compose
*compose
= (Compose
*)data
;
4501 compose_exec_ext_editor(compose
);
4504 static void compose_destroy_cb(GtkWidget
*widget
, Compose
*compose
)
4506 compose_destroy(compose
);
4509 static void compose_cut_cb(Compose
*compose
)
4511 if (compose
->focused_editable
&&
4512 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
4513 gtk_editable_cut_clipboard
4514 (GTK_EDITABLE(compose
->focused_editable
));
4517 static void compose_copy_cb(Compose
*compose
)
4519 if (compose
->focused_editable
&&
4520 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
4521 gtk_editable_copy_clipboard
4522 (GTK_EDITABLE(compose
->focused_editable
));
4525 static void compose_paste_cb(Compose
*compose
)
4527 if (compose
->focused_editable
&&
4528 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
4529 gtk_editable_paste_clipboard
4530 (GTK_EDITABLE(compose
->focused_editable
));
4533 static void compose_allsel_cb(Compose
*compose
)
4535 if (compose
->focused_editable
&&
4536 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
4537 gtk_editable_select_region
4538 (GTK_EDITABLE(compose
->focused_editable
), 0, -1);
4541 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
4543 if (GTK_IS_EDITABLE(widget
))
4544 compose
->focused_editable
= widget
;
4547 static void compose_changed_cb(GtkEditable
*editable
, Compose
*compose
)
4549 if (compose
->modified
== FALSE
) {
4550 compose
->modified
= TRUE
;
4551 compose_set_title(compose
);
4555 static void compose_button_press_cb(GtkWidget
*widget
, GdkEventButton
*event
,
4558 gtk_stext_set_point(GTK_STEXT(widget
),
4559 gtk_editable_get_position(GTK_EDITABLE(widget
)));
4563 static void compose_key_press_cb(GtkWidget
*widget
, GdkEventKey
*event
,
4566 gtk_stext_set_point(GTK_STEXT(widget
),
4567 gtk_editable_get_position(GTK_EDITABLE(widget
)));
4571 static void compose_toggle_to_cb(gpointer data
, guint action
,
4574 Compose
*compose
= (Compose
*)data
;
4576 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4577 gtk_widget_show(compose
->to_hbox
);
4578 gtk_widget_show(compose
->to_entry
);
4579 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 1, 4);
4580 compose
->use_to
= TRUE
;
4582 gtk_widget_hide(compose
->to_hbox
);
4583 gtk_widget_hide(compose
->to_entry
);
4584 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 1, 0);
4585 gtk_widget_queue_resize(compose
->table_vbox
);
4586 compose
->use_to
= FALSE
;
4589 if (addressbook_get_target_compose() == compose
)
4590 addressbook_set_target_compose(compose
);
4593 static void compose_toggle_cc_cb(gpointer data
, guint action
,
4596 Compose
*compose
= (Compose
*)data
;
4598 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4599 gtk_widget_show(compose
->cc_hbox
);
4600 gtk_widget_show(compose
->cc_entry
);
4601 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 3, 4);
4602 compose
->use_cc
= TRUE
;
4604 gtk_widget_hide(compose
->cc_hbox
);
4605 gtk_widget_hide(compose
->cc_entry
);
4606 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 3, 0);
4607 gtk_widget_queue_resize(compose
->table_vbox
);
4608 compose
->use_cc
= FALSE
;
4611 if (addressbook_get_target_compose() == compose
)
4612 addressbook_set_target_compose(compose
);
4615 static void compose_toggle_bcc_cb(gpointer data
, guint action
,
4618 Compose
*compose
= (Compose
*)data
;
4620 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4621 gtk_widget_show(compose
->bcc_hbox
);
4622 gtk_widget_show(compose
->bcc_entry
);
4623 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 4, 4);
4624 compose
->use_bcc
= TRUE
;
4626 gtk_widget_hide(compose
->bcc_hbox
);
4627 gtk_widget_hide(compose
->bcc_entry
);
4628 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 4, 0);
4629 gtk_widget_queue_resize(compose
->table_vbox
);
4630 compose
->use_bcc
= FALSE
;
4633 if (addressbook_get_target_compose() == compose
)
4634 addressbook_set_target_compose(compose
);
4637 static void compose_toggle_replyto_cb(gpointer data
, guint action
,
4640 Compose
*compose
= (Compose
*)data
;
4642 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4643 gtk_widget_show(compose
->reply_hbox
);
4644 gtk_widget_show(compose
->reply_entry
);
4645 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 5, 4);
4646 compose
->use_replyto
= TRUE
;
4648 gtk_widget_hide(compose
->reply_hbox
);
4649 gtk_widget_hide(compose
->reply_entry
);
4650 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 5, 0);
4651 gtk_widget_queue_resize(compose
->table_vbox
);
4652 compose
->use_replyto
= FALSE
;
4656 static void compose_toggle_followupto_cb(gpointer data
, guint action
,
4659 Compose
*compose
= (Compose
*)data
;
4661 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4662 gtk_widget_show(compose
->followup_hbox
);
4663 gtk_widget_show(compose
->followup_entry
);
4664 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 6, 4);
4665 compose
->use_followupto
= TRUE
;
4667 gtk_widget_hide(compose
->followup_hbox
);
4668 gtk_widget_hide(compose
->followup_entry
);
4669 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 6, 0);
4670 gtk_widget_queue_resize(compose
->table_vbox
);
4671 compose
->use_followupto
= FALSE
;
4675 static void compose_toggle_attach_cb(gpointer data
, guint action
,
4678 Compose
*compose
= (Compose
*)data
;
4680 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4681 gtk_widget_ref(compose
->edit_vbox
);
4683 gtk_container_remove(GTK_CONTAINER(compose
->vbox2
),
4684 compose
->edit_vbox
);
4685 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
4686 gtk_box_pack_start(GTK_BOX(compose
->vbox2
), compose
->paned
,
4688 gtk_widget_show(compose
->paned
);
4690 gtk_widget_unref(compose
->edit_vbox
);
4691 gtk_widget_unref(compose
->paned
);
4693 compose
->use_attach
= TRUE
;
4695 gtk_widget_ref(compose
->paned
);
4696 gtk_widget_ref(compose
->edit_vbox
);
4698 gtk_container_remove(GTK_CONTAINER(compose
->vbox2
),
4700 gtk_container_remove(GTK_CONTAINER(compose
->paned
),
4701 compose
->edit_vbox
);
4702 gtk_box_pack_start(GTK_BOX(compose
->vbox2
),
4703 compose
->edit_vbox
, TRUE
, TRUE
, 0);
4705 gtk_widget_unref(compose
->edit_vbox
);
4707 compose
->use_attach
= FALSE
;
4712 static void compose_toggle_sign_cb(gpointer data
, guint action
,
4715 Compose
*compose
= (Compose
*)data
;
4717 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
4718 compose
->use_signing
= TRUE
;
4720 compose
->use_signing
= FALSE
;
4723 static void compose_toggle_encrypt_cb(gpointer data
, guint action
,
4726 Compose
*compose
= (Compose
*)data
;
4728 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
4729 compose
->use_encryption
= TRUE
;
4731 compose
->use_encryption
= FALSE
;
4733 #endif /* USE_GPGME */
4735 static void compose_toggle_ruler_cb(gpointer data
, guint action
,
4738 Compose
*compose
= (Compose
*)data
;
4740 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
4741 gtk_widget_show(compose
->ruler_hbox
);
4742 prefs_common
.show_ruler
= TRUE
;
4744 gtk_widget_hide(compose
->ruler_hbox
);
4745 gtk_widget_queue_resize(compose
->edit_vbox
);
4746 prefs_common
.show_ruler
= FALSE
;
4750 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
4751 GdkDragContext
*drag_context
,
4754 GtkSelectionData
*data
,
4759 Compose
*compose
= (Compose
*)user_data
;
4762 list
= uri_list_extract_filenames((const gchar
*)data
->data
);
4763 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
)
4764 compose_attach_append(compose
, (const gchar
*)tmp
->data
,
4766 list_free_strings(list
);
4770 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
4771 GdkDragContext
*drag_context
,
4774 GtkSelectionData
*data
,
4779 Compose
*compose
= (Compose
*)user_data
;
4782 list
= uri_list_extract_filenames((const gchar
*)data
->data
);
4783 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
)
4784 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
4785 list_free_strings(list
);
4789 static void to_activated(GtkWidget
*widget
, Compose
*compose
)
4791 if (GTK_WIDGET_VISIBLE(compose
->newsgroups_entry
))
4792 gtk_widget_grab_focus(compose
->newsgroups_entry
);
4794 gtk_widget_grab_focus(compose
->subject_entry
);
4797 static void newsgroups_activated(GtkWidget
*widget
, Compose
*compose
)
4799 gtk_widget_grab_focus(compose
->subject_entry
);
4802 static void subject_activated(GtkWidget
*widget
, Compose
*compose
)
4804 if (GTK_WIDGET_VISIBLE(compose
->cc_entry
))
4805 gtk_widget_grab_focus(compose
->cc_entry
);
4806 else if (GTK_WIDGET_VISIBLE(compose
->bcc_entry
))
4807 gtk_widget_grab_focus(compose
->bcc_entry
);
4808 else if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
4809 gtk_widget_grab_focus(compose
->reply_entry
);
4810 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
4811 gtk_widget_grab_focus(compose
->followup_entry
);
4813 gtk_widget_grab_focus(compose
->text
);
4816 static void cc_activated(GtkWidget
*widget
, Compose
*compose
)
4818 if (GTK_WIDGET_VISIBLE(compose
->bcc_entry
))
4819 gtk_widget_grab_focus(compose
->bcc_entry
);
4820 else if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
4821 gtk_widget_grab_focus(compose
->reply_entry
);
4822 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
4823 gtk_widget_grab_focus(compose
->followup_entry
);
4825 gtk_widget_grab_focus(compose
->text
);
4828 static void bcc_activated(GtkWidget
*widget
, Compose
*compose
)
4830 if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
4831 gtk_widget_grab_focus(compose
->reply_entry
);
4832 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
4833 gtk_widget_grab_focus(compose
->followup_entry
);
4835 gtk_widget_grab_focus(compose
->text
);
4838 static void replyto_activated(GtkWidget
*widget
, Compose
*compose
)
4840 if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
4841 gtk_widget_grab_focus(compose
->followup_entry
);
4843 gtk_widget_grab_focus(compose
->text
);
4846 static void followupto_activated(GtkWidget
*widget
, Compose
*compose
)
4848 gtk_widget_grab_focus(compose
->text
);
4851 static void compose_toggle_return_receipt_cb(gpointer data
, guint action
,
4854 Compose
*compose
= (Compose
*)data
;
4856 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
4857 compose
->return_receipt
= TRUE
;
4859 compose
->return_receipt
= FALSE
;
4862 static gchar
*compose_quote_fmt (Compose
*compose
,
4867 gchar
* quote_str
= NULL
;
4869 if (qmark
!= NULL
) {
4872 quote_fmt_init(msginfo
, NULL
);
4873 quote_fmt_scan_string(qmark
);
4876 p
= quote_fmt_get_buffer();
4879 (_("Quote mark format error."));
4882 quote_str
= alloca(strlen(p
) + 1);
4883 strcpy(quote_str
, p
);
4887 quote_fmt_init(msginfo
, quote_str
);
4888 quote_fmt_scan_string(fmt
);
4891 if (quote_fmt_get_buffer() == NULL
)
4893 (_("Message reply/forward format error."));
4895 return quote_fmt_get_buffer();
4898 static void template_apply_cb(gchar
*s
, gpointer data
)
4900 Compose
*compose
= (Compose
*)data
;
4901 GtkSText
*text
= GTK_STEXT(compose
->text
);
4906 gchar
*old_tmpl
= s
;
4910 if(compose
->replyinfo
=NULL
) {
4911 gtk_stext_freeze(text
);
4912 gtk_stext_set_point(text
, 0);
4913 gtk_stext_forward_delete(text
, gtk_stext_get_length(text
));
4914 gtk_stext_insert(text
, NULL
, NULL
, NULL
, s
, -1);
4915 gtk_stext_thaw(text
);
4920 parsed_text
= g_new(gchar
, strlen(s
)*2 + 1);
4924 *parsed_text
++ = '\\';
4925 *parsed_text
++ = 'n';
4928 *parsed_text
++ = *s
++;
4931 *parsed_text
= '\0';
4933 if (prefs_common
.quotemark
&& *prefs_common
.quotemark
)
4934 qmark
= prefs_common
.quotemark
;
4938 quote_str
= compose_quote_fmt(compose
, compose
->replyinfo
, tmpl
, qmark
);
4939 if (quote_str
!= NULL
) {
4940 gtk_stext_freeze(text
);
4941 gtk_stext_set_point(text
, 0);
4942 gtk_stext_forward_delete(text
, gtk_stext_get_length(text
));
4943 gtk_stext_insert(text
, NULL
, NULL
, NULL
, quote_str
, -1);
4944 gtk_stext_thaw(text
);
4951 static void template_select_cb(gpointer data
, guint action
,
4954 template_select(&template_apply_cb
, data
);