d6baa824e1998a036bec785e0d4106d7e55ab935
[modest] / src / gtk / modest-edit-msg-window.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 #include <glib/gi18n.h>
30 #include <tny-account-store.h>
31 #include "modest-edit-msg-window.h"
32 #include "modest-widget-memory.h"
33 #include "modest-mail-operation.h"
34 #include "modest-tny-platform-factory.h"
35 #include "modest-tny-msg-actions.h"
36 #include <tny-simple-list.h>
37
38 static void  modest_edit_msg_window_class_init   (ModestEditMsgWindowClass *klass);
39 static void  modest_edit_msg_window_init         (ModestEditMsgWindow *obj);
40 static void  modest_edit_msg_window_finalize     (GObject *obj);
41
42 /* list my signals */
43 enum {
44         /* MY_SIGNAL_1, */
45         /* MY_SIGNAL_2, */
46         LAST_SIGNAL
47 };
48
49 typedef struct _ModestEditMsgWindowPrivate ModestEditMsgWindowPrivate;
50 struct _ModestEditMsgWindowPrivate {
51
52         ModestWidgetFactory *factory;
53         TnyPlatformFactory *fact;
54         
55         GtkWidget      *toolbar, *menubar;
56         GtkWidget      *msg_body;
57         GtkWidget      *from_field, *to_field, *cc_field, *bcc_field,
58                        *subject_field;
59 };
60 #define MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
61                                                     MODEST_TYPE_EDIT_MSG_WINDOW, \
62                                                     ModestEditMsgWindowPrivate))
63 /* globals */
64 static GtkWindowClass *parent_class = NULL;
65
66 /* uncomment the following if you have defined any signals */
67 /* static guint signals[LAST_SIGNAL] = {0}; */
68
69 GType
70 modest_edit_msg_window_get_type (void)
71 {
72         static GType my_type = 0;
73         if (!my_type) {
74                 static const GTypeInfo my_info = {
75                         sizeof(ModestEditMsgWindowClass),
76                         NULL,           /* base init */
77                         NULL,           /* base finalize */
78                         (GClassInitFunc) modest_edit_msg_window_class_init,
79                         NULL,           /* class finalize */
80                         NULL,           /* class data */
81                         sizeof(ModestEditMsgWindow),
82                         1,              /* n_preallocs */
83                         (GInstanceInitFunc) modest_edit_msg_window_init,
84                         NULL
85                 };
86                 my_type = g_type_register_static (GTK_TYPE_WINDOW,
87                                                   "ModestEditMsgWindow",
88                                                   &my_info, 0);
89         }
90         return my_type;
91 }
92
93 static void
94 modest_edit_msg_window_class_init (ModestEditMsgWindowClass *klass)
95 {
96         GObjectClass *gobject_class;
97         gobject_class = (GObjectClass*) klass;
98
99         parent_class            = g_type_class_peek_parent (klass);
100         gobject_class->finalize = modest_edit_msg_window_finalize;
101
102         g_type_class_add_private (gobject_class, sizeof(ModestEditMsgWindowPrivate));
103
104         /* signal definitions go here, e.g.: */
105 /*      signals[MY_SIGNAL_1] = */
106 /*              g_signal_new ("my_signal_1",....); */
107 /*      signals[MY_SIGNAL_2] = */
108 /*              g_signal_new ("my_signal_2",....); */
109 /*      etc. */
110 }
111
112 static void
113 modest_edit_msg_window_init (ModestEditMsgWindow *obj)
114 {
115         ModestEditMsgWindowPrivate *priv;
116         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
117
118         priv->fact = modest_tny_platform_factory_get_instance ();
119         priv->factory = NULL;
120         priv->toolbar = NULL;
121         priv->menubar = NULL;
122 }
123
124
125
126 static void
127 save_settings (ModestEditMsgWindow *self)
128 {
129         ModestEditMsgWindowPrivate *priv;
130         ModestConf *conf;
131
132         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
133         conf = modest_tny_platform_factory_get_modest_conf_instance (priv->fact);
134
135         modest_widget_memory_save_settings (conf, GTK_WIDGET(self),
136                                             "modest-edit-msg-window");
137 }
138
139
140 static void
141 restore_settings (ModestEditMsgWindow *self)
142 {
143         ModestEditMsgWindowPrivate *priv;
144         ModestConf *conf;
145
146         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
147         conf = modest_tny_platform_factory_get_modest_conf_instance (priv->fact);
148
149         modest_widget_memory_restore_settings (conf, GTK_WIDGET(self),
150                                                "modest-edit-msg-window");
151 }
152
153         
154
155 static void
156 on_menu_quit (ModestEditMsgWindow *self, guint action, GtkWidget *widget)
157 {
158         save_settings (self);
159         gtk_widget_destroy (GTK_WIDGET(self));
160 }
161
162
163
164
165
166 /* Our menu, an array of GtkItemFactoryEntry structures that defines each menu item */
167 static GtkItemFactoryEntry menu_items[] = {
168         { "/_File",             NULL,                   NULL,           0, "<Branch>" ,NULL},
169         { "/File/_New",         "<control>N",           NULL,           0, "<StockItem>", GTK_STOCK_NEW },
170         { "/File/_Open",        "<control>O",           NULL,           0, "<StockItem>", GTK_STOCK_OPEN },
171         { "/File/_Save",        "<control>S",           NULL,           0, "<StockItem>", GTK_STOCK_SAVE },
172         { "/File/Save _As",     NULL,                   NULL,           0, "<Item>", NULL} ,
173         { "/File/Save Draft",   "<control><shift>S",    NULL,           0, "<Item>",NULL },
174
175
176         { "/File/sep1",         NULL,                   NULL,           0, "<Separator>" ,NULL },
177         { "/File/_Quit",        "<CTRL>Q",              on_menu_quit,   0, "<StockItem>", GTK_STOCK_QUIT },
178
179         { "/_Edit",             NULL,                   NULL,           0, "<Branch>" ,NULL },
180         { "/Edit/_Undo",        "<CTRL>Z",              NULL,           0, "<StockItem>", GTK_STOCK_UNDO },
181         { "/Edit/_Redo",        "<shift><CTRL>Z",       NULL,           0, "<StockItem>", GTK_STOCK_REDO },
182         { "/File/sep1",         NULL,                   NULL,           0, "<Separator>",NULL },
183         { "/Edit/Cut",          "<control>X",           NULL,           0, "<StockItem>", GTK_STOCK_CUT  },
184         { "/Edit/Copy",         "<CTRL>C",              NULL,           0, "<StockItem>", GTK_STOCK_COPY },
185         { "/Edit/Paste",        NULL,                   NULL,           0, "<StockItem>", GTK_STOCK_PASTE},
186         { "/Edit/sep1",         NULL,                   NULL,           0, "<Separator>",NULL },
187         { "/Edit/Delete",       "<CTRL>Q",              NULL,           0, "<Item>" ,NULL },
188         { "/Edit/Select all",   "<CTRL>A",              NULL,           0, "<Item>" ,NULL },
189         { "/Edit/Deselect all",  "<Shift><CTRL>A",      NULL,           0, "<Item>",NULL },
190
191         { "/_View",             NULL,           NULL,                   0, "<Branch>",NULL },
192         { "/View/To-field",          NULL,              NULL,           0, "<CheckItem>",NULL },
193         
194         { "/View/Cc-field:",          NULL,             NULL,           0, "<CheckItem>",NULL },
195         { "/View/Bcc-field:",          NULL,            NULL,           0, "<CheckItem>",NULL },
196         
197         
198         { "/_Insert",             NULL,         NULL,           0, "<Branch>",NULL },
199 /*      { "/Actions/_Reply",    NULL,                   NULL,           0, "<Item>" }, */
200 /*      { "/Actions/_Forward",  NULL,                   NULL,           0, "<Item>" }, */
201 /*      { "/Actions/_Bounce",   NULL,                   NULL,           0, "<Item>" },   */
202         
203         { "/_Format",            NULL,                  NULL,           0, "<Branch>",NULL }
204 /*      { "/Options/_Accounts",  NULL,                  on_menu_accounts,0, "<Item>" }, */
205 /*      { "/Options/_Contacts",  NULL,                  NULL,           0, "<Item>" }, */
206
207
208 /*      { "/_Help",         NULL,                       NULL,           0, "<Branch>" }, */
209 /*      { "/_Help/About",   NULL,                       on_menu_about,  0, "<StockItem>", GTK_STOCK_ABOUT}, */
210 };
211
212 static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
213
214
215 static GtkWidget *
216 menubar_new (ModestEditMsgWindow *self)
217 {
218         GtkItemFactory *item_factory;
219         GtkAccelGroup *accel_group;
220         
221         /* Make an accelerator group (shortcut keys) */
222         accel_group = gtk_accel_group_new ();
223         
224         /* Make an ItemFactory (that makes a menubar) */
225         item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
226                                              accel_group);
227         
228         /* This function generates the menu items. Pass the item factory,
229            the number of items in the array, the array itself, and any
230            callback data for the the menu items. */
231         gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, self);
232         
233         ///* Attach the new accelerator group to the window. */
234         gtk_window_add_accel_group (GTK_WINDOW (self), accel_group);
235         
236         /* Finally, return the actual menu bar created by the item factory. */
237         return gtk_item_factory_get_widget (item_factory, "<main>");
238 }
239
240
241 static void
242 send_mail (ModestEditMsgWindow *self)
243 {
244         const gchar *to, *cc, *bcc, *subject;
245         gchar *body, *from;
246         ModestEditMsgWindowPrivate *priv;
247         TnyTransportAccount *transport_account;
248         ModestMailOperation *mail_operation;
249         ModestAccountData *data;
250         
251         GtkTextBuffer *buf;
252         GtkTextIter b, e;
253         
254         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
255         data = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
256
257         /* don't free these (except from) */
258         from    =  g_strdup_printf ("%s <%s>", data->full_name, data->email) ;
259         to      =  gtk_entry_get_text (GTK_ENTRY(priv->to_field));
260         cc      =  gtk_entry_get_text (GTK_ENTRY(priv->cc_field));
261         bcc     =  gtk_entry_get_text (GTK_ENTRY(priv->bcc_field));
262         subject =  gtk_entry_get_text (GTK_ENTRY(priv->subject_field));
263         
264         /* don't unref */
265         buf   =  gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->msg_body));
266         
267         gtk_text_buffer_get_bounds (buf, &b, &e);
268         body  = gtk_text_buffer_get_text (buf, &b, &e,
269                                           FALSE); /* free this one */
270
271         /* FIXME: Code added just for testing. The transport_account
272            should be provided by the account manager, maybe using
273            _get_current_account () or _get_default_account
274            (TRANSPORT_ACCOUNT). These methods do not exist currently. */
275         {
276                 TnyList *accounts;
277                 TnyIterator *iter;
278                 TnyAccountStore *account_store;
279
280                 accounts = TNY_LIST(tny_simple_list_new ());
281                 account_store = tny_platform_factory_new_account_store (priv->fact);
282                 tny_account_store_get_accounts (account_store, accounts,
283                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
284
285                 iter = tny_list_create_iterator(accounts);
286                 tny_iterator_first (iter);
287                 if (tny_iterator_is_done (iter)) {
288                         /* FIXME: Add error handling through mail operation */
289                         g_printerr("modest: no transport accounts defined\n");
290                         g_free (body);
291                         return;
292                 }
293                 transport_account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current(iter));
294         }
295
296         mail_operation = modest_mail_operation_new (TNY_ACCOUNT (transport_account));
297
298         modest_mail_operation_send_new_mail (mail_operation,
299                                              from, to, cc, bcc,
300                                              subject, body, NULL);
301         /* Clean up */
302         g_object_unref (mail_operation);
303         g_free (from);
304         g_free (body);
305 }
306
307
308 static void
309 on_toolbar_button_clicked (ModestToolbar *toolbar, ModestToolbarButton button_id,
310                            ModestEditMsgWindow *self)
311 {
312         switch (button_id) {
313         case MODEST_TOOLBAR_BUTTON_MAIL_SEND:
314                 send_mail (self);
315                 save_settings (self);
316                 gtk_widget_destroy (GTK_WIDGET(self));
317                 break;
318                 
319         case MODEST_TOOLBAR_BUTTON_REPLY:
320         case MODEST_TOOLBAR_BUTTON_REPLY_ALL:
321         case MODEST_TOOLBAR_BUTTON_FORWARD:
322         case MODEST_TOOLBAR_BUTTON_SEND_RECEIVE:
323         case MODEST_TOOLBAR_BUTTON_NEXT:
324         case MODEST_TOOLBAR_BUTTON_PREV:
325         case MODEST_TOOLBAR_BUTTON_DELETE:
326
327         default:
328                 g_printerr ("modest: key %d pressed\n", button_id);
329         }
330 }
331
332
333
334
335 static ModestToolbar*
336 toolbar_new (ModestEditMsgWindow *self)
337 {
338         int i;
339         ModestToolbar *toolbar;
340         GSList *buttons = NULL;
341         ModestEditMsgWindowPrivate *priv;
342
343         ModestToolbarButton button_ids[] = {
344                 MODEST_TOOLBAR_BUTTON_MAIL_SEND
345         };              
346         
347         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
348
349         for (i = 0 ; i != sizeof(button_ids) / sizeof(ModestToolbarButton); ++i)
350                 buttons = g_slist_append (buttons, GINT_TO_POINTER(button_ids[i]));
351         
352         toolbar = modest_widget_factory_get_edit_toolbar (priv->factory, buttons);
353         g_slist_free (buttons);
354
355         g_signal_connect (G_OBJECT(toolbar), "button_clicked",
356                           G_CALLBACK(on_toolbar_button_clicked), self);
357         
358         return toolbar;
359 }
360
361
362 static void
363 init_window (ModestEditMsgWindow *obj)
364 {
365         GtkWidget *to_button, *cc_button, *bcc_button; 
366         GtkWidget *header_table;
367         GtkWidget *main_vbox;
368         
369         ModestEditMsgWindowPrivate *priv;
370         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
371
372         to_button     = gtk_button_new_with_label (_("To..."));
373         cc_button     = gtk_button_new_with_label (_("Cc..."));
374         bcc_button    = gtk_button_new_with_label (_("Bcc..."));
375
376         priv->from_field    = modest_widget_factory_get_combo_box (priv->factory,
377                                                                    MODEST_COMBO_BOX_TYPE_TRANSPORTS);
378         priv->to_field      = gtk_entry_new_with_max_length (40);
379         priv->cc_field      = gtk_entry_new_with_max_length (40);
380         priv->bcc_field     = gtk_entry_new_with_max_length (40);
381         priv->subject_field = gtk_entry_new_with_max_length (40);
382         
383         header_table = gtk_table_new (5,2, FALSE);
384         
385         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("From:")),
386                           0,1,0,1, GTK_SHRINK, 0, 0, 0);
387         gtk_table_attach (GTK_TABLE(header_table), to_button,     0,1,1,2, GTK_SHRINK, 0, 0, 0);
388         gtk_table_attach (GTK_TABLE(header_table), cc_button,     0,1,2,3, GTK_SHRINK, 0, 0, 0);
389         gtk_table_attach (GTK_TABLE(header_table), bcc_button,    0,1,3,4, GTK_SHRINK, 0, 0, 0);
390         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("Subject:")),
391                           0,1,4,5, GTK_SHRINK, 0, 0, 0);
392
393         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->from_field,   1,2,0,1);
394         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->to_field,     1,2,1,2);
395         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->cc_field,     1,2,2,3);
396         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->bcc_field,    1,2,3,4);
397         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->subject_field,1,2,4,5);
398
399         priv->msg_body = gtk_text_view_new ();
400         
401         main_vbox = gtk_vbox_new  (FALSE, 6);
402
403         priv->menubar = menubar_new (obj);
404         priv->toolbar = GTK_WIDGET(toolbar_new (obj));
405
406         gtk_box_pack_start (GTK_BOX(main_vbox), priv->menubar, FALSE, FALSE, 0);
407         gtk_box_pack_start (GTK_BOX(main_vbox), priv->toolbar, FALSE, FALSE, 0);
408         gtk_box_pack_start (GTK_BOX(main_vbox), header_table, FALSE, FALSE, 6);
409         gtk_box_pack_start (GTK_BOX(main_vbox), priv->msg_body, TRUE, TRUE, 6);
410
411         gtk_widget_show_all (GTK_WIDGET(main_vbox));
412         gtk_container_add (GTK_CONTAINER(obj), main_vbox);
413 }
414         
415
416
417 static void
418 modest_edit_msg_window_finalize (GObject *obj)
419 {
420         ModestEditMsgWindowPrivate *priv;
421
422         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
423
424         g_object_unref (G_OBJECT(priv->factory));
425         priv->factory = NULL;
426         
427         G_OBJECT_CLASS(parent_class)->finalize (obj);
428
429 }
430
431
432
433 static gboolean
434 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestEditMsgWindow *self)
435 {
436         save_settings (self);
437         return FALSE;
438 }
439
440
441 GtkWidget*
442 modest_edit_msg_window_new (ModestWidgetFactory *factory,
443                             ModestEditType type, TnyMsg *msg)
444 {
445         GObject *obj;
446         ModestEditMsgWindowPrivate *priv;
447
448         g_return_val_if_fail (factory, NULL);
449         g_return_val_if_fail (type < MODEST_EDIT_TYPE_NUM, NULL);
450         g_return_val_if_fail (!(type==MODEST_EDIT_TYPE_NEW && msg), NULL); 
451         g_return_val_if_fail (!(type!=MODEST_EDIT_TYPE_NEW && !msg), NULL);     
452         
453         obj = g_object_new(MODEST_TYPE_EDIT_MSG_WINDOW, NULL);
454         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
455
456         g_object_ref (factory);
457         priv->factory = factory;
458
459         init_window (MODEST_EDIT_MSG_WINDOW(obj));
460
461         restore_settings (MODEST_EDIT_MSG_WINDOW(obj));
462         
463         gtk_window_set_title (GTK_WINDOW(obj), "Modest");
464         gtk_window_set_icon  (GTK_WINDOW(obj),
465                               modest_icon_factory_get_icon (MODEST_APP_ICON));
466
467         g_signal_connect (G_OBJECT(obj), "delete-event",
468                           G_CALLBACK(on_delete_event), obj);
469         
470         if (msg) {
471                 /* Testing code. Should be into a set_msg method */
472                 TnyHeader *header;
473                 GtkTextBuffer *buf;
474
475                 header = tny_msg_get_header (msg);
476                 gtk_entry_set_text (GTK_ENTRY(priv->to_field),
477                                     tny_header_get_to (header));
478                 gtk_entry_set_text (GTK_ENTRY(priv->cc_field),
479                                     tny_header_get_cc (header));
480                 gtk_entry_set_text (GTK_ENTRY(priv->bcc_field),
481                                     tny_header_get_bcc (header));
482                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field),
483                                     tny_header_get_subject (header));   
484
485                 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->msg_body));
486                 gtk_text_buffer_set_text (buf,
487                                           (const gchar *) modest_tny_msg_actions_find_body (msg, FALSE),
488                                           -1);
489
490                 /* TODO: lower priority, select in the From: combo to
491                    the value that comes from msg */
492
493                 /* TODO: set attachments */
494         }
495
496         return GTK_WIDGET (obj);
497 }