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