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