* added licensing boiler plate to source files
[modest] / src / gtk / modest-ui-message-editor.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 #include <gtk/gtk.h>
32 #include <glade/glade.h>
33 #include <glib/gi18n.h>
34 #include <string.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif /*HAVE_CONFIG_H*/
39
40 /* TODO: put in auto* */
41 #include <tny-text-buffer-stream.h>
42 #include <tny-msg-folder.h>
43
44 #include "../modest-ui.h"
45 #include "../modest-window-mgr.h"
46 #include "../modest-account-mgr.h"
47 #include "../modest-account-mgr.h"
48 #include "../modest-identity-mgr.h"
49
50 #include "../modest-tny-account-store.h"
51 #include "../modest-tny-folder-tree-view.h"
52 #include "../modest-tny-header-tree-view.h"
53 #include "../modest-tny-msg-view.h"
54 #include "../modest-tny-transport-actions.h"
55 #include "../modest-tny-store-actions.h"
56
57 #include "../modest-text-utils.h"
58 #include "../modest-tny-msg-actions.h"
59
60 #include "../modest-tny-attachment.h"
61
62 #include "../modest-editor-window.h"
63
64 #include "modest-ui-glade.h"
65 #include "modest-ui-wizard.h"
66
67 #include "modest-ui-message-editor.h"
68
69
70 static void on_attach_button_clicked (GtkWidget *widget, ModestEditorWindow *modest_editwin);
71
72 static void on_send_button_clicked (GtkWidget *widget, ModestEditorWindow *modest_editwin);
73
74
75 typedef struct {
76         ModestUI *modest_ui;
77         ModestEditorWindow *edit_win;
78         GladeXML *glade_xml;
79         GList *attachments;
80 } EditWinData;
81
82
83 static gboolean close_edit_confirm_dialog(ModestEditorWindow *edit_win)
84 {
85         GtkWidget *mdialog;
86         gint res;
87
88         mdialog = gtk_message_dialog_new(GTK_WINDOW(edit_win),
89                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
90                         GTK_MESSAGE_QUESTION,
91                         GTK_BUTTONS_YES_NO,
92                         _("Message was modified.\nDiscard Changes?"));
93         gtk_widget_show_all (mdialog);
94
95         res=gtk_dialog_run(GTK_DIALOG(mdialog));
96         gtk_widget_destroy (mdialog);
97         if (res == GTK_RESPONSE_YES)
98                 return TRUE;
99         else
100                 return FALSE;
101 }
102
103 static void
104 close_edit_window (GtkWidget *win, GdkEvent *event, gpointer data)
105 {
106         ModestEditorWindow *edit_win;
107         ModestUIPrivate *priv;
108         EditWinData *win_data;
109
110         edit_win = (ModestEditorWindow *)data;
111         win_data = modest_editor_window_get_data(edit_win);
112         priv = MODEST_UI_GET_PRIVATE(win_data->modest_ui);
113
114         // g_message("window was %s modified", modest_editor_window_get_modified(edit_win) ? "" : "not");
115
116         if (modest_editor_window_get_modified(edit_win)) {
117                 if (close_edit_confirm_dialog(edit_win)) {
118                         gtk_widget_hide (GTK_WIDGET(edit_win));
119                         modest_window_mgr_unregister(priv->modest_window_mgr, G_OBJECT(edit_win));
120                         gtk_widget_destroy(GTK_WIDGET(edit_win));
121                         g_message("closing window");
122                 } else {
123                         g_message("not closing window");
124                 }
125         } else {
126                 gtk_widget_hide (GTK_WIDGET(edit_win));
127                 modest_window_mgr_unregister(priv->modest_window_mgr, G_OBJECT(edit_win));
128                 gtk_widget_destroy(GTK_WIDGET(edit_win));
129                 g_message("closing window");
130         }
131 }
132
133
134 GtkContainer
135 *modest_ui_new_editor_window (ModestUI *modest_ui, gpointer *user_data)
136 {
137         GtkWidget       *top_container;
138         GladeXML                *glade_xml;
139         EditWinData             *win_data;
140
141         glade_xml = glade_xml_new(MODEST_GLADE, "new_mail_top_container", NULL);
142         if (!glade_xml)
143                 return NULL;
144
145         win_data = g_malloc(sizeof(EditWinData));
146         win_data->modest_ui = modest_ui;
147         win_data->glade_xml = glade_xml;
148         win_data->attachments = NULL;
149
150         *user_data = win_data;
151
152         top_container = glade_xml_get_widget(glade_xml, "new_mail_top_container");
153         if (!top_container) {
154                 g_object_unref(G_OBJECT(glade_xml));
155                 return NULL;
156         }
157         
158         return GTK_CONTAINER(top_container);
159 }
160
161
162 gboolean
163 modest_ui_editor_window_set_to_header(const gchar *to, gpointer window_data)
164 {
165         GladeXML *glade_xml;
166         GtkWidget *w;
167         EditWinData *win_data;
168
169         win_data = (EditWinData *)window_data;
170         glade_xml = win_data->glade_xml;
171         w = glade_xml_get_widget(glade_xml, "to_entry");
172         gtk_entry_set_text(GTK_ENTRY(w), to);
173
174         return TRUE;
175 }
176
177
178 gboolean
179 modest_ui_editor_window_set_cc_header(const gchar *cc, gpointer window_data)
180 {
181         GladeXML *glade_xml;
182         // GtkWidget *w;
183         EditWinData *win_data;
184
185         win_data = (EditWinData *)window_data;
186         glade_xml = win_data->glade_xml;
187 /*
188         w = glade_xml_get_widget(glade_xml, "cc_entry");
189         gtk_entry_set_text(GTK_ENTRY(w), cc);
190 */
191         return TRUE;
192 }
193
194
195 gboolean
196 modest_ui_editor_window_set_bcc_header(const gchar *bcc, gpointer window_data)
197 {
198         GladeXML *glade_xml;
199         // GtkWidget *w;
200         EditWinData *win_data;
201
202         win_data = (EditWinData *)window_data;
203         glade_xml = win_data->glade_xml;
204 /*
205         w = glade_xml_get_widget(glade_xml, "bcc_entry");
206         gtk_entry_set_text(GTK_ENTRY(w), bcc);
207 */
208         return TRUE;
209 }
210
211
212 gboolean
213 modest_ui_editor_window_set_subject_header(const gchar *subject, gpointer window_data)
214 {
215         GladeXML *glade_xml;
216         GtkWidget *w;
217         EditWinData *win_data;
218
219         win_data = (EditWinData *)window_data;
220         glade_xml = win_data->glade_xml;
221
222         w = glade_xml_get_widget(glade_xml, "subject_entry");
223         gtk_entry_set_text(GTK_ENTRY(w), subject);
224
225         return TRUE;
226 }
227
228
229 gboolean
230 modest_ui_editor_window_set_body(const gchar *body, gpointer window_data)
231 {
232         GladeXML *glade_xml;
233         GtkWidget *body_view;
234         GtkTextBuffer *buf;
235         EditWinData *win_data;
236
237         win_data = (EditWinData *)window_data;
238         glade_xml = win_data->glade_xml;
239
240         body_view = glade_xml_get_widget(glade_xml, "body_view");
241         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(body_view));
242
243         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(buf), body, -1);
244
245         return TRUE;
246 }
247
248
249 gboolean
250 modest_ui_editor_window_update_attachments(gpointer window_data)
251 {
252         GladeXML *glade_xml;
253         
254         glade_xml = ((EditWinData *) window_data)->glade_xml;
255
256         //body_view = glade_xml_get_widget(glade_xml, "body_view");
257         
258         return TRUE;
259 }
260
261
262 static
263 void on_editor_entry_changed(GtkEditable *editable,
264                                             gpointer     user_data)
265 {
266         GtkWidget *edit_win;
267         EditWinData *windata;
268
269         edit_win = (GtkWidget *)user_data;
270         windata = (EditWinData *)modest_editor_window_get_data(MODEST_EDITOR_WINDOW(edit_win));
271
272         modest_editor_window_set_modified(MODEST_EDITOR_WINDOW(edit_win), TRUE);
273 }
274
275
276 static
277 void on_editor_buffer_changed (GtkTextBuffer *textbuffer,
278                                             gpointer       user_data)
279 {
280         GtkWidget *edit_win;
281         
282         edit_win = (GtkWidget *)user_data;
283         modest_editor_window_set_modified(MODEST_EDITOR_WINDOW(edit_win), TRUE);
284 }
285
286
287 static void
288 new_editor_with_presets (ModestUI *modest_ui, const gchar *to_header,
289                                                         const gchar *cc_header, const gchar *bcc_header,
290                                                         const gchar *subject_header, const gchar *body,
291                             const GList *attachments)
292 {
293         GtkWidget *edit_win;
294         GladeXML *glade_xml;
295         GtkWidget *btn, *w;
296         GtkTextBuffer *buf;
297         EditWinData *windata;
298         ModestUIPrivate *priv;
299         gint height, width;
300
301         g_return_if_fail (modest_ui);
302
303         edit_win = modest_editor_window_new(modest_ui);
304         windata = (EditWinData *)modest_editor_window_get_data(MODEST_EDITOR_WINDOW(edit_win));
305         g_return_if_fail(windata);
306
307         glade_xml = windata->glade_xml;
308         btn = glade_xml_get_widget (glade_xml, "toolb_send");
309         g_signal_connect (btn, "clicked", G_CALLBACK(on_send_button_clicked),
310                           edit_win);
311         btn = glade_xml_get_widget (glade_xml, "toolb_attach");
312         g_signal_connect (btn, "clicked", G_CALLBACK(on_attach_button_clicked),
313                           edit_win);
314
315         w = glade_xml_get_widget (glade_xml, "to_entry");
316         g_signal_connect(w, "changed", G_CALLBACK(on_editor_entry_changed), edit_win);
317         w = glade_xml_get_widget (glade_xml, "subject_entry");
318         g_signal_connect(w, "changed", G_CALLBACK(on_editor_entry_changed), edit_win);
319         w = glade_xml_get_widget (glade_xml, "body_view");
320         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
321         g_signal_connect(buf, "changed", G_CALLBACK(on_editor_buffer_changed), edit_win);
322
323         g_signal_connect (edit_win, "destroy-event", G_CALLBACK(close_edit_window),
324                           edit_win);
325         g_signal_connect (edit_win, "delete-event", G_CALLBACK(close_edit_window),
326                           edit_win);
327
328         priv = MODEST_UI_GET_PRIVATE(modest_ui);
329         height = modest_conf_get_int (priv->modest_conf,
330                                           MODEST_CONF_EDIT_WINDOW_HEIGHT, NULL);
331         width  = modest_conf_get_int (priv->modest_conf,
332                                           MODEST_CONF_EDIT_WINDOW_WIDTH, NULL);
333
334         // g_message("new editor win@%dx%d", width, height);
335
336         gtk_widget_set_usize (GTK_WIDGET(edit_win), width, height);
337         if (strlen(subject_header) > 0)
338                 gtk_window_set_title (GTK_WINDOW(edit_win), subject_header);
339         else
340                 gtk_window_set_title (GTK_WINDOW(edit_win), _("Untitled"));
341
342         modest_window_mgr_register(priv->modest_window_mgr, G_OBJECT(edit_win), MODEST_EDIT_WINDOW, 0);
343
344         modest_editor_window_set_to_header(MODEST_EDITOR_WINDOW(edit_win), to_header);
345         modest_editor_window_set_cc_header(MODEST_EDITOR_WINDOW(edit_win), cc_header);
346         modest_editor_window_set_bcc_header(MODEST_EDITOR_WINDOW(edit_win), bcc_header);
347         modest_editor_window_set_subject_header(MODEST_EDITOR_WINDOW(edit_win), subject_header);
348         modest_editor_window_set_body(MODEST_EDITOR_WINDOW(edit_win), body);
349         modest_editor_window_set_attachments(MODEST_EDITOR_WINDOW(edit_win), attachments);
350
351         modest_editor_window_set_modified(MODEST_EDITOR_WINDOW(edit_win), FALSE);
352
353         gtk_widget_show(edit_win);
354 }
355
356
357 void
358 on_new_mail_clicked (GtkWidget *widget, gpointer user_data)
359 {
360         ModestUI *modest_ui = (ModestUI *) user_data;
361
362         new_editor_with_presets(modest_ui, "", "", "", "", "", NULL);
363 }
364
365 void
366 ui_on_mailto_clicked (GtkWidget *widget, const gchar *uri, gpointer user_data)
367 {
368         ModestUI *modest_ui = (ModestUI *) user_data;
369         
370         new_editor_with_presets(modest_ui, uri, "", "", "", "", NULL);
371 }
372
373
374 void
375 quoted_send_msg (ModestUI *modest_ui, quoted_send_type qstype)
376 {
377         GtkTreeSelection *sel;
378         GtkTreeModel *model;
379         GtkTreeIter iter;
380
381         TnyMsgHeaderIface *header;
382
383         ModestTnyHeaderTreeView *header_view;
384         ModestTnyMsgView *msg_view;
385         ModestUIPrivate *priv;
386
387         const TnyMsgIface *msg;
388         const TnyMsgFolderIface *folder;
389         GString *re_sub;
390         const gchar *subject, *from;
391         gchar *unquoted, *quoted;
392         time_t sent_date;
393         gint line_limit = 76;
394         
395         GList *attachments = NULL;
396
397         g_return_if_fail (modest_ui);
398
399         priv = MODEST_UI_GET_PRIVATE(modest_ui);
400
401         msg_view = MODEST_TNY_MSG_VIEW(priv->message_view);
402         g_return_if_fail (msg_view);
403
404         header_view = MODEST_TNY_HEADER_TREE_VIEW(priv->header_view);
405         g_return_if_fail (header_view);
406
407         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(header_view));
408         g_return_if_fail (sel);
409
410         if (!gtk_tree_selection_get_selected (sel, &model, &iter)) {
411                 g_warning("nothing to reply to");
412                 return;
413         }
414
415         gtk_tree_model_get (model, &iter,
416                             TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
417                             &header, -1);
418
419         if (!header) {
420                 g_warning("no header");
421                 return;
422         }
423
424         folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
425         if (!folder) {
426                 g_warning ("cannot find folder");
427                 return;
428         }
429
430         msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder), header);
431         if (!msg) {
432                 g_warning ("cannot find msg");
433                 return;
434         }
435
436         subject = tny_msg_header_iface_get_subject(header);
437         re_sub = g_string_new(subject);
438         /* FIXME: honor replyto, cc */
439         from = tny_msg_header_iface_get_from(header);
440         sent_date = tny_msg_header_iface_get_date_sent(header);
441
442         unquoted = modest_tny_msg_view_get_selected_text(msg_view);
443         quoted = modest_tny_msg_actions_quote(msg, from, sent_date, line_limit, unquoted);
444
445         switch (qstype) {
446                 case QUOTED_SEND_REPLY:
447                         g_string_prepend(re_sub, _("Re: "));
448                         new_editor_with_presets(modest_ui, from, /* cc */ "", /* bcc */ "", re_sub->str, quoted, attachments);
449                         break;
450                 case QUOTED_SEND_FORWARD:
451                         attachments = modest_tny_attachment_new_list_from_msg(msg, FALSE);
452                         g_string_prepend(re_sub, _("Fwd: "));
453                         new_editor_with_presets(modest_ui, /* from */ "", /* cc */ "", /* bcc */ "", re_sub->str, quoted, attachments);
454                         break;
455                 case QUOTED_SEND_FORWARD_ATTACHED:
456                         attachments = modest_tny_attachment_new_list_from_msg(msg, TRUE);
457                         g_string_prepend(re_sub, _("Fwd: "));
458                         new_editor_with_presets(modest_ui, /* from */ "", /* cc */ "", /* bcc */ "", re_sub->str, "", attachments);
459                         break;
460                 default:
461                         break;
462         }
463         g_free(quoted);
464         g_free(unquoted);
465         g_string_free(re_sub, TRUE);
466 }
467
468
469 static void
470 on_attach_button_clicked (GtkWidget *widget, ModestEditorWindow *modest_editwin)
471 {
472         /* open file selector */
473         GtkWidget *dialog;
474         ModestTnyAttachment *attachment;
475         gchar *filename = NULL;
476         
477         dialog = gtk_file_chooser_dialog_new ("Open File",
478                                                                                   GTK_WINDOW(modest_editwin),
479                                                                                   GTK_FILE_CHOOSER_ACTION_OPEN,
480                                                                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
481                                                                                   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
482                                                                                   NULL);
483
484         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
485         {
486
487                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
488                 printf ("file:%s\n", filename);
489         }
490         gtk_widget_destroy (dialog);
491       
492         /* check file */
493         if (!filename)
494                 return;
495         
496         attachment = modest_tny_attachment_new();
497         modest_tny_attachment_set_filename(attachment, filename);
498         modest_tny_attachment_guess_mime_type(attachment);
499         
500         modest_editor_window_attach_file(modest_editwin, attachment);
501         
502         g_free (filename);
503 }
504
505
506 static void
507 on_send_button_clicked (GtkWidget *widget, ModestEditorWindow *modest_editwin)
508 {
509         ModestTnyTransportActions *actions;
510         ModestUI *modest_ui;
511         ModestUIPrivate *priv;
512         GtkWidget *to_entry, *subject_entry, *body_view;
513         const gchar *to, *subject, *email_from;
514         gchar *body;
515         GtkTextIter start, end;
516         GtkTextBuffer *buf;
517         TnyAccountStoreIface *account_store;
518         const GList *transport_accounts;
519         TnyTransportAccountIface *transport_account;
520         ModestIdentityMgr *id_mgr;
521         EditWinData *win_data;
522         GList * attachments;
523
524
525         win_data = modest_editor_window_get_data(modest_editwin);
526         modest_ui = win_data->modest_ui;
527
528         g_return_if_fail (modest_ui);
529
530         actions = MODEST_TNY_TRANSPORT_ACTIONS
531                 (modest_tny_transport_actions_new ());
532
533         priv = MODEST_UI_GET_PRIVATE(modest_ui);
534
535         account_store = priv->account_store;
536         transport_accounts =
537                 tny_account_store_iface_get_transport_accounts (account_store);
538         if (!transport_accounts) {
539                 g_message ("cannot send message: no transport account defined");
540                 return;
541         } else /* take the first one! */
542                 transport_account =
543                         TNY_TRANSPORT_ACCOUNT_IFACE(transport_accounts->data);
544
545         to_entry      = glade_xml_get_widget (win_data->glade_xml, "to_entry");
546         subject_entry = glade_xml_get_widget (win_data->glade_xml, "subject_entry");
547         body_view     = glade_xml_get_widget (win_data->glade_xml, "body_view");
548
549         to      = gtk_entry_get_text (GTK_ENTRY(to_entry));
550         subject = gtk_entry_get_text (GTK_ENTRY(subject_entry));
551
552         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(body_view));
553         gtk_text_buffer_get_bounds (buf, &start, &end);
554         body    = gtk_text_buffer_get_text (buf, &start, &end, FALSE);
555
556         id_mgr = priv->modest_id_mgr;
557         email_from = modest_identity_mgr_get_identity_string(id_mgr,
558                                                              MODEST_IDENTITY_DEFAULT_IDENTITY,
559                                                              MODEST_IDENTITY_EMAIL, NULL);
560         attachments = modest_editor_window_get_attachments(modest_editwin);
561         if (!email_from)
562                 email_from = "";
563         
564         g_message("sending \"%s\" %s ==> %s", subject, email_from, to);
565
566         modest_tny_transport_actions_send_message (actions,
567                                                    transport_account,
568                                                    email_from,
569                                                    to, "", "", subject,
570                                                    body,
571                                                    attachments);
572
573         modest_editor_window_set_attachments(modest_editwin, NULL); /* This unrefs them, too. */
574         g_free (body);
575         g_object_unref (G_OBJECT(actions));
576
577         gtk_widget_hide (GTK_WIDGET(modest_editwin));
578         modest_window_mgr_unregister(priv->modest_window_mgr, G_OBJECT(modest_editwin));
579         if (GTK_IS_WIDGET(modest_editwin)) {
580                 gtk_widget_destroy(GTK_WIDGET(modest_editwin));
581         } else
582                 g_warning("editor window has vanished!");
583 }