/* TMut * Copyright (C) 2006-2007 Philip Van Hoof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with self library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include "tmut-msg-creator.h" #include "tmut-account-store.h" #include #include #include #include #include #include #include #include #include #include static GObjectClass *parent_class = NULL; typedef struct _TMutMsgCreatorPriv TMutMsgCreatorPriv; struct _TMutMsgCreatorPriv { TMutShellWindow *shell; GtkEntry *subject_entry, *to_entry; GtkTextView *message_textview; GtkButton *send_button, *attachments_button; GtkComboBox *accounts_combo; TnyList *attachments; TnyAccountStore *account_store; gint account_created_signal, account_deleted_signal; }; #define TMUT_MSG_CREATOR_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TMUT_TYPE_MSG_CREATOR, TMutMsgCreatorPriv)) /* Translator: this is the text on the button next to the Attachments label of * the message composer window. It should show how many items are attached to * the new message. */ #define ATTACH_TXT _("%d attached") typedef struct { TnySendQueue *send_queue; TnyHeader *header; } OnResponseInfo; static GtkWidget *rem_dialog = NULL; static void on_response (GtkDialog *dialog, gint response, gpointer user_data) { OnResponseInfo *info = (OnResponseInfo *) user_data; if (response == GTK_RESPONSE_YES) { TnyFolder *outbox = tny_send_queue_get_outbox (info->send_queue); tny_folder_remove_msg (outbox, info->header, NULL); tny_folder_sync (outbox, TRUE, NULL); g_object_unref (outbox); } g_object_unref (info->send_queue); g_object_unref (info->header); gtk_widget_destroy (GTK_WIDGET (dialog)); rem_dialog = NULL; g_slice_free (OnResponseInfo, info); return; } static void on_send_queue_error_happened (TnySendQueue *self, TnyHeader *header, TnyMsg *msg, GError *err, gpointer user_data) { if (header) { gchar *sub = tny_header_dup_subject (header); gchar *str = g_strdup_printf (_("%s.\nDo you want to remove the message (%s)?"), err->message, sub); g_free (sub); if (!rem_dialog) { OnResponseInfo *info = g_slice_new (OnResponseInfo); rem_dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_YES_NO, str); g_free (str); info->send_queue = (TnySendQueue *) g_object_ref (self); info->header = (TnyHeader *) g_object_ref (header); g_signal_connect (G_OBJECT (rem_dialog), "response", G_CALLBACK (on_response), info); gtk_widget_show_all (rem_dialog); } } return; } static TnySendQueue* get_send_queue_for (TnyAccount *account, TMutMsgCreator *self) { TnySendQueue *retval; if (!send_queues) send_queues = g_hash_table_new (g_direct_hash, g_direct_equal); retval = g_hash_table_lookup (send_queues, (gconstpointer) account); if (!retval) { TnySendQueue *send_queue = tny_camel_send_queue_new (TNY_CAMEL_TRANSPORT_ACCOUNT (account)); g_signal_connect (G_OBJECT (send_queue), "error-happened", G_CALLBACK (on_send_queue_error_happened), NULL); g_hash_table_insert (send_queues, account, send_queue); retval = send_queue; } return TNY_SEND_QUEUE (g_object_ref (retval)); } typedef struct { TnyAccount *account; TnyStream *stream; TnySendQueue *send_queue; TnyMsg *msg; TnyList *attachments; } CreateAndSendMsgInfo; static gpointer create_and_send_msg_thread (gpointer user_data) { CreateAndSendMsgInfo *info = (CreateAndSendMsgInfo *) user_data; if (info->attachments && tny_list_get_length (info->attachments) > 0) { TnyIterator *iter = tny_list_create_iterator (info->attachments); TnyMimePart *msg_part = tny_camel_mime_part_new (); tny_mime_part_construct (msg_part, info->stream, "text/plain", "7bit"); tny_mime_part_add_part (TNY_MIME_PART (info->msg), msg_part); g_object_unref (msg_part); while (!tny_iterator_is_done (iter)) { TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter)); tny_mime_part_add_part (TNY_MIME_PART (info->msg), part); g_object_unref (part); tny_iterator_next (iter); } g_object_unref (iter); } else tny_mime_part_construct (TNY_MIME_PART (info->msg), info->stream, "text/plain", "7bit"); tny_send_queue_add (info->send_queue, info->msg, NULL); if (info->attachments) g_object_unref (info->attachments); g_object_unref (info->send_queue); g_object_unref (info->msg); g_object_unref (info->stream); g_object_unref (info->account); g_slice_free (CreateAndSendMsgInfo, info); return NULL; } static void on_send_button_clicked (GtkButton *button, gpointer user_data) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (user_data); GtkTreeIter iter; TnyAccount *account = NULL; GtkTreeModel *model = gtk_combo_box_get_model (priv->accounts_combo); gboolean not_ready = FALSE; if (gtk_combo_box_get_active_iter (priv->accounts_combo, &iter)) { gtk_tree_model_get (model, &iter, TNY_GTK_ACCOUNT_LIST_MODEL_INSTANCE_COLUMN, &account, -1); if (account) { CreateAndSendMsgInfo *info = g_slice_new0 (CreateAndSendMsgInfo); TnySendQueue *send_queue = get_send_queue_for (account, user_data); GtkTextBuffer *buffer = gtk_text_view_get_buffer (priv->message_textview); GtkTextIter start_iter, end_iter; gchar *text = NULL; TnyStream *stream = tny_camel_mem_stream_new (); TnyMsg *msg = tny_platform_factory_new_msg (platfact); TnyHeader *header = tny_msg_get_header (msg); tny_header_set_subject (header, gtk_entry_get_text (priv->subject_entry)); if (TNY_IS_CAMEL_TRANSPORT_ACCOUNT (account)) { TnyCamelTransportAccount *ta = (TnyCamelTransportAccount *) account; if (tny_camel_transport_account_get_from (ta)) tny_header_set_from (header, tny_camel_transport_account_get_from (ta)); else tny_header_set_from (header, tny_account_get_name (account)); } else tny_header_set_from (header, tny_account_get_name (account)); tny_header_set_to (header, gtk_entry_get_text (priv->to_entry)); g_object_unref (header); gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter); text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE); tny_stream_write (stream, text, strlen (text)); g_free (text); tny_stream_reset (stream); tny_mime_part_set_header_pair (TNY_MIME_PART (msg), "X-Mailer", "TMut - " VERSION); if (priv->attachments) info->attachments = tny_list_copy (priv->attachments); else info->attachments = NULL; info->send_queue = g_object_ref (send_queue); info->msg = g_object_ref (msg); info->stream = g_object_ref (stream); info->account = g_object_ref (account); g_thread_create (create_and_send_msg_thread, info, FALSE, NULL); g_object_unref (stream); g_object_unref (msg); g_object_unref (send_queue); g_object_unref (account); } else not_ready = TRUE; } else not_ready = TRUE; if (not_ready) { GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (priv->shell), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("E-mail client not ready for sending E-mails. Configure " "an account that is suitable for sending first.")); gtk_widget_show_all (dialog); } else tmut_shell_window_back (priv->shell); return; } static void set_reply_msg_process_part (TnyMimePart *part, GString *str) { TnyStream *stream = tny_mime_part_get_stream (part); gchar buffer[1024]; gint i = 0, s = 1024; s = tny_stream_read (stream, buffer, 1024); for (i=0; i < s && i < 1024; i++) { if (buffer[i] == '\n') g_string_append (str, "\n> "); else g_string_append_c (str, buffer[i]); } g_object_unref (stream); } static void set_reply_msg_walk_parts (TnyMimePart *my_part, GString *str) { TnyIterator *iter; TnyList *parts = tny_simple_list_new (); gboolean end = FALSE; tny_mime_part_get_parts (my_part, parts); iter = tny_list_create_iterator (parts); while (!tny_iterator_is_done (iter) && !end) { TnyList *nparts = tny_simple_list_new (); TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter); if (tny_mime_part_content_type_is (part, "text/plain")) { set_reply_msg_process_part (part, str); end = TRUE; } /* Recurse it too */ if (!end) set_reply_msg_walk_parts (part, str); g_object_unref (part); tny_iterator_next (iter); } g_object_unref (parts); g_object_unref (iter); return; } static void set_reply_msg (TMutMsgCreator *self, TnyMsg *msg, GString *str) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); if (tny_mime_part_content_type_is (TNY_MIME_PART (msg), "text/plain")) set_reply_msg_process_part (TNY_MIME_PART (msg), str); else set_reply_msg_walk_parts (TNY_MIME_PART (msg), str); } void tmut_msg_creator_add_attachment (TMutMsgCreator *self, TnyMimePart *part) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); gchar *str = NULL; tny_list_prepend (priv->attachments, G_OBJECT (part)); str = g_strdup_printf (ATTACH_TXT, tny_list_get_length (priv->attachments)); gtk_button_set_label (priv->attachments_button, str); g_free (str); return; } void tmut_msg_creator_remove_attachment (TMutMsgCreator *self, TnyMimePart *part) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); gchar *str = NULL; tny_list_remove (priv->attachments, G_OBJECT (part)); str = g_strdup_printf (ATTACH_TXT, tny_list_get_length (priv->attachments)); gtk_button_set_label (priv->attachments_button, str); g_free (str); return; } static void on_attachments_button_clicked (GtkButton *button, gpointer user_data) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (user_data); g_print ("ATTACHMENTS EDITOR\n"); return; } void tmut_msg_creator_set_forward_msg (TMutMsgCreator *self, TnyMsg *msg) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (priv->message_textview); TnyHeader *header = tny_msg_get_header (msg); /* Translator: this Fwd-text is the default subject field when creating * a forward of a message */ gchar *osubject = (gchar *) tny_header_dup_subject (header), *str = g_strdup_printf (_("[Fwd: %s]"), osubject?osubject:_("No subject")); g_free (osubject); gtk_entry_set_text (priv->subject_entry, (const gchar*) str); g_free (str); tmut_msg_creator_add_attachment (self, TNY_MIME_PART (msg)); gtk_text_buffer_set_text (text_buffer, "", 0); return; } void tmut_msg_creator_set_reply_msg (TMutMsgCreator *self, TnyMsg *msg) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (priv->message_textview); TnyHeader *header = tny_msg_get_header (msg); GString *gstr = g_string_new (""); /* Translator: this Re-text is the default subject field when creating * a reply of a message */ gchar *osubject = (gchar *) tny_header_dup_subject (header), *str = g_strdup_printf (_("[Re: %s]"), osubject?osubject:_("No subject")); g_free (osubject); gtk_entry_set_text (priv->subject_entry, (const gchar*) str); g_free (str); str = tny_header_dup_from (header); g_string_printf (gstr, _("On %s, %s wrote:\n"), _get_readable_date (tny_header_get_date_sent (header)), str); g_free (str); set_reply_msg (self, msg, gstr); if (str) { gtk_text_buffer_set_text (text_buffer, gstr->str, gstr->len); g_string_free (gstr, TRUE); } return; } static void on_account_created (TMutAccountStore *store, TnyAccount *account, TMutMsgCreator *self) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); TnyList *model = TNY_LIST (gtk_combo_box_get_model (priv->accounts_combo)); tny_list_prepend (model, (GObject *) account); } static void on_account_deleted (TMutAccountStore *store, TnyAccount *account, TMutMsgCreator *self) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); TnyList *model = TNY_LIST (gtk_combo_box_get_model (priv->accounts_combo)); tny_list_remove (model, (GObject *) account); } static void disconnect_account_store (TMutMsgCreatorPriv *priv) { g_signal_handler_disconnect (priv->account_store, priv->account_created_signal); g_signal_handler_disconnect (priv->account_store, priv->account_deleted_signal); g_object_unref (priv->account_store); priv->account_store = NULL; } static void tmut_msg_creator_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); TnyList *accounts = TNY_LIST (tny_gtk_account_list_model_new ()); if (priv->account_store) disconnect_account_store (priv); priv->account_store = TNY_ACCOUNT_STORE (g_object_ref (account_store)); tny_account_store_get_accounts (priv->account_store, accounts, TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS); priv->account_created_signal = g_signal_connect (G_OBJECT (priv->account_store), "account_created", G_CALLBACK (on_account_created), self); priv->account_deleted_signal = g_signal_connect (G_OBJECT (priv->account_store), "account_deleted", G_CALLBACK (on_account_deleted), self); gtk_combo_box_set_model (priv->accounts_combo, GTK_TREE_MODEL (accounts)); gtk_combo_box_set_active (priv->accounts_combo, 0); return; } /** * tmut_msg_creator_new: * Create a GtkCreator that implements #TnyMsgCreator * * Return value: a new #TnyMsgCreator instance implemented for Gtk+ **/ TMutMsgCreator * tmut_msg_creator_new (void) { TMutMsgCreator *self = g_object_new (TMUT_TYPE_MSG_CREATOR, NULL); return TMUT_MSG_CREATOR (self); } static void tmut_msg_creator_instance_init (GTypeInstance *instance, gpointer g_class) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (instance); GtkWidget *vbox1; GtkWidget *table1; GtkWidget *label1; GtkWidget *label2; GtkWidget *scrolledwindow1; GtkCellRenderer *renderer; GtkTreeViewColumn *column; gchar *str = NULL; priv->account_store = NULL; priv->account_created_signal = -1; priv->account_deleted_signal = -1; priv->attachments = tny_simple_list_new (); vbox1 = GTK_WIDGET (instance); table1 = gtk_table_new (4, 2, FALSE); gtk_widget_show (table1); gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, TRUE, 0); gtk_table_set_col_spacings (GTK_TABLE (table1), 2); label1 = gtk_label_new (_("From")); gtk_widget_show (label1); gtk_table_attach (GTK_TABLE (table1), label1, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label1), TRUE); gtk_misc_set_alignment (GTK_MISC (label1), 0, 0.5); label1 = gtk_label_new (_("To")); gtk_widget_show (label1); gtk_table_attach (GTK_TABLE (table1), label1, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label1), TRUE); gtk_misc_set_alignment (GTK_MISC (label1), 0, 0.5); label2 = gtk_label_new (_("Subject")); gtk_widget_show (label2); gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label2), TRUE); gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); label2 = gtk_label_new (_("Attached")); gtk_widget_show (label2); gtk_table_attach (GTK_TABLE (table1), label2, 0, 1, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label2), TRUE); gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); priv->accounts_combo = GTK_COMBO_BOX (gtk_combo_box_new ()); gtk_widget_show (GTK_WIDGET (priv->accounts_combo)); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (priv->accounts_combo), renderer, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT (priv->accounts_combo), renderer, "text", TNY_GTK_ACCOUNT_LIST_MODEL_NAME_COLUMN, NULL); gtk_table_attach (GTK_TABLE (table1), GTK_WIDGET (priv->accounts_combo), 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); priv->to_entry = GTK_ENTRY (gtk_entry_new ()); gtk_widget_show (GTK_WIDGET (priv->to_entry)); gtk_table_attach (GTK_TABLE (table1), GTK_WIDGET (priv->to_entry), 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); priv->subject_entry = GTK_ENTRY (gtk_entry_new ()); gtk_widget_show (GTK_WIDGET (priv->subject_entry)); gtk_table_attach (GTK_TABLE (table1), GTK_WIDGET (priv->subject_entry), 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); str = g_strdup_printf (ATTACH_TXT, 0); priv->attachments_button = GTK_BUTTON (gtk_button_new_with_label (str)); g_free (str); gtk_widget_show (GTK_WIDGET (priv->attachments_button)); gtk_table_attach (GTK_TABLE (table1), GTK_WIDGET (priv->attachments_button), 1, 2, 3, 4, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow1); gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_SHADOW_IN); priv->message_textview = GTK_TEXT_VIEW (gtk_text_view_new ()); gtk_widget_show (GTK_WIDGET (priv->message_textview)); gtk_container_add (GTK_CONTAINER (scrolledwindow1), GTK_WIDGET (priv->message_textview)); priv->send_button = GTK_BUTTON (gtk_button_new_with_mnemonic (_("Send"))); gtk_widget_show (GTK_WIDGET (priv->send_button)); gtk_box_pack_start (GTK_BOX (vbox1), GTK_WIDGET (priv->send_button), FALSE, FALSE, 0); g_signal_connect (G_OBJECT (priv->send_button), "clicked", G_CALLBACK (on_send_button_clicked), instance); g_signal_connect (G_OBJECT (priv->attachments_button), "clicked", G_CALLBACK (on_attachments_button_clicked), instance); return; } static void tmut_msg_creator_finalize (GObject *object) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (object); if (priv->account_store) disconnect_account_store (priv); g_object_unref (priv->attachments); (*parent_class->finalize) (object); return; } static void tny_account_store_view_init (gpointer g, gpointer iface_data) { TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g; klass->set_account_store= tmut_msg_creator_set_account_store; return; } static void tmut_msg_creator_class_init (TMutMsgCreatorClass *class) { GObjectClass *object_class; parent_class = g_type_class_peek_parent (class); object_class = (GObjectClass*) class; object_class->finalize = tmut_msg_creator_finalize; g_type_class_add_private (object_class, sizeof (TMutMsgCreatorPriv)); return; } TMutShellWindow* tmut_msg_creator_get_window (TMutShellChild *self) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); return priv->shell; } void tmut_msg_creator_set_window (TMutShellChild *self, TMutShellWindow *window) { TMutMsgCreatorPriv *priv = TMUT_MSG_CREATOR_GET_PRIVATE (self); priv->shell = window; } static void tmut_shell_child_init (gpointer g, gpointer iface_data) { TMutShellChildIface *klass = (TMutShellChildIface *)g; klass->get_window= tmut_msg_creator_get_window; klass->set_window= tmut_msg_creator_set_window; return; } GType tmut_msg_creator_get_type (void) { static GType type = 0; if (G_UNLIKELY(type == 0)) { static const GTypeInfo info = { sizeof (TMutMsgCreatorClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) tmut_msg_creator_class_init, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ sizeof (TMutMsgCreator), 0, /* n_preallocs */ tmut_msg_creator_instance_init, /* instance_init */ NULL }; static const GInterfaceInfo tny_account_store_view_info = { (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */ NULL, /* interface_finalize */ NULL /* interface_data */ }; static const GInterfaceInfo tmut_shell_child_info = { (GInterfaceInitFunc) tmut_shell_child_init, /* interface_init */ NULL, /* interface_finalize */ NULL /* interface_data */ }; type = g_type_register_static (GTK_TYPE_VBOX, "TMutMsgCreator", &info, 0); g_type_add_interface_static (type, TNY_TYPE_ACCOUNT_STORE_VIEW, &tny_account_store_view_info); g_type_add_interface_static (type, TMUT_TYPE_SHELL_CHILD, &tmut_shell_child_info); } return type; }