* gtk/modest-edit-msg-window.c:
[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 (conf, G_OBJECT(self), "modest-edit-msg-window");
136 }
137
138
139 static void
140 restore_settings (ModestEditMsgWindow *self)
141 {
142         ModestEditMsgWindowPrivate *priv;
143         ModestConf *conf;
144
145         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
146         conf = modest_tny_platform_factory_get_modest_conf_instance (priv->fact);
147
148         modest_widget_memory_restore (conf, G_OBJECT(self), "modest-edit-msg-window");
149 }
150
151         
152
153 static void
154 on_menu_quit (ModestEditMsgWindow *self, guint action, GtkWidget *widget)
155 {
156         save_settings (self);
157         gtk_widget_destroy (GTK_WIDGET(self));
158 }
159
160
161
162
163
164 /* Our menu, an array of GtkItemFactoryEntry structures that defines each menu item */
165 static GtkItemFactoryEntry menu_items[] = {
166         { "/_File",             NULL,                   NULL,           0, "<Branch>" ,NULL},
167         { "/File/_New",         "<control>N",           NULL,           0, "<StockItem>", GTK_STOCK_NEW },
168         { "/File/_Open",        "<control>O",           NULL,           0, "<StockItem>", GTK_STOCK_OPEN },
169         { "/File/_Save",        "<control>S",           NULL,           0, "<StockItem>", GTK_STOCK_SAVE },
170         { "/File/Save _As",     NULL,                   NULL,           0, "<Item>", NULL} ,
171         { "/File/Save Draft",   "<control><shift>S",    NULL,           0, "<Item>",NULL },
172
173
174         { "/File/sep1",         NULL,                   NULL,           0, "<Separator>" ,NULL },
175         { "/File/_Quit",        "<CTRL>Q",              on_menu_quit,   0, "<StockItem>", GTK_STOCK_QUIT },
176
177         { "/_Edit",             NULL,                   NULL,           0, "<Branch>" ,NULL },
178         { "/Edit/_Undo",        "<CTRL>Z",              NULL,           0, "<StockItem>", GTK_STOCK_UNDO },
179         { "/Edit/_Redo",        "<shift><CTRL>Z",       NULL,           0, "<StockItem>", GTK_STOCK_REDO },
180         { "/File/sep1",         NULL,                   NULL,           0, "<Separator>",NULL },
181         { "/Edit/Cut",          "<control>X",           NULL,           0, "<StockItem>", GTK_STOCK_CUT  },
182         { "/Edit/Copy",         "<CTRL>C",              NULL,           0, "<StockItem>", GTK_STOCK_COPY },
183         { "/Edit/Paste",        NULL,                   NULL,           0, "<StockItem>", GTK_STOCK_PASTE},
184         { "/Edit/sep1",         NULL,                   NULL,           0, "<Separator>",NULL },
185         { "/Edit/Delete",       "<CTRL>Q",              NULL,           0, "<Item>" ,NULL },
186         { "/Edit/Select all",   "<CTRL>A",              NULL,           0, "<Item>" ,NULL },
187         { "/Edit/Deselect all",  "<Shift><CTRL>A",      NULL,           0, "<Item>",NULL },
188
189         { "/_View",             NULL,           NULL,                   0, "<Branch>",NULL },
190         { "/View/To-field",          NULL,              NULL,           0, "<CheckItem>",NULL },
191         
192         { "/View/Cc-field:",          NULL,             NULL,           0, "<CheckItem>",NULL },
193         { "/View/Bcc-field:",          NULL,            NULL,           0, "<CheckItem>",NULL },
194         
195         
196         { "/_Insert",             NULL,         NULL,           0, "<Branch>",NULL },
197 /*      { "/Actions/_Reply",    NULL,                   NULL,           0, "<Item>" }, */
198 /*      { "/Actions/_Forward",  NULL,                   NULL,           0, "<Item>" }, */
199 /*      { "/Actions/_Bounce",   NULL,                   NULL,           0, "<Item>" },   */
200         
201         { "/_Format",            NULL,                  NULL,           0, "<Branch>",NULL }
202 /*      { "/Options/_Accounts",  NULL,                  on_menu_accounts,0, "<Item>" }, */
203 /*      { "/Options/_Contacts",  NULL,                  NULL,           0, "<Item>" }, */
204
205
206 /*      { "/_Help",         NULL,                       NULL,           0, "<Branch>" }, */
207 /*      { "/_Help/About",   NULL,                       on_menu_about,  0, "<StockItem>", GTK_STOCK_ABOUT}, */
208 };
209
210 static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
211
212
213 static GtkWidget *
214 menubar_new (ModestEditMsgWindow *self)
215 {
216         GtkItemFactory *item_factory;
217         GtkAccelGroup *accel_group;
218         
219         /* Make an accelerator group (shortcut keys) */
220         accel_group = gtk_accel_group_new ();
221         
222         /* Make an ItemFactory (that makes a menubar) */
223         item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
224                                              accel_group);
225         
226         /* This function generates the menu items. Pass the item factory,
227            the number of items in the array, the array itself, and any
228            callback data for the the menu items. */
229         gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, self);
230         
231         ///* Attach the new accelerator group to the window. */
232         gtk_window_add_accel_group (GTK_WINDOW (self), accel_group);
233         
234         /* Finally, return the actual menu bar created by the item factory. */
235         return gtk_item_factory_get_widget (item_factory, "<main>");
236 }
237
238
239 static void
240 send_mail (ModestEditMsgWindow *self)
241 {
242         const gchar *to, *cc, *bcc, *subject;
243         gchar *body, *from;
244         ModestEditMsgWindowPrivate *priv;
245         TnyTransportAccount *transport_account;
246         ModestMailOperation *mail_operation;
247         ModestAccountData *data;
248         
249         GtkTextBuffer *buf;
250         GtkTextIter b, e;
251         
252         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
253         data = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
254
255         /* don't free these (except from) */
256         from    =  g_strdup_printf ("%s <%s>", data->full_name, data->email) ;
257         to      =  gtk_entry_get_text (GTK_ENTRY(priv->to_field));
258         cc      =  gtk_entry_get_text (GTK_ENTRY(priv->cc_field));
259         bcc     =  gtk_entry_get_text (GTK_ENTRY(priv->bcc_field));
260         subject =  gtk_entry_get_text (GTK_ENTRY(priv->subject_field));
261         
262         /* don't unref */
263         buf   =  gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->msg_body));
264         
265         gtk_text_buffer_get_bounds (buf, &b, &e);
266         body  = gtk_text_buffer_get_text (buf, &b, &e,
267                                           FALSE); /* free this one */
268
269         /* FIXME: Code added just for testing. The transport_account
270            should be provided by the account manager, maybe using
271            _get_current_account () or _get_default_account
272            (TRANSPORT_ACCOUNT). These methods do not exist currently. */
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         }
293
294         mail_operation = modest_mail_operation_new ();
295
296         modest_mail_operation_send_new_mail (mail_operation,
297                                              transport_account,
298                                              from, to, cc, bcc,
299                                              subject, body, NULL);
300         /* Clean up */
301         g_object_unref (mail_operation);
302         g_free (from);
303         g_free (body);
304 }
305
306
307 static void
308 on_toolbar_button_clicked (ModestToolbar *toolbar, ModestToolbarButton button_id,
309                            ModestEditMsgWindow *self)
310 {
311         switch (button_id) {
312         case MODEST_TOOLBAR_BUTTON_MAIL_SEND:
313                 send_mail (self);
314                 save_settings (self);
315                 gtk_widget_destroy (GTK_WIDGET(self));
316                 break;
317                 
318         case MODEST_TOOLBAR_BUTTON_REPLY:
319         case MODEST_TOOLBAR_BUTTON_REPLY_ALL:
320         case MODEST_TOOLBAR_BUTTON_FORWARD:
321         case MODEST_TOOLBAR_BUTTON_SEND_RECEIVE:
322         case MODEST_TOOLBAR_BUTTON_NEXT:
323         case MODEST_TOOLBAR_BUTTON_PREV:
324         case MODEST_TOOLBAR_BUTTON_DELETE:
325
326         default:
327                 g_printerr ("modest: key %d pressed\n", button_id);
328         }
329 }
330
331
332
333
334 static ModestToolbar*
335 toolbar_new (ModestEditMsgWindow *self)
336 {
337         int i;
338         ModestToolbar *toolbar;
339         GSList *buttons = NULL;
340         ModestEditMsgWindowPrivate *priv;
341
342         ModestToolbarButton button_ids[] = {
343                 MODEST_TOOLBAR_BUTTON_MAIL_SEND
344         };              
345         
346         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(self);
347
348         for (i = 0 ; i != sizeof(button_ids) / sizeof(ModestToolbarButton); ++i)
349                 buttons = g_slist_append (buttons, GINT_TO_POINTER(button_ids[i]));
350         
351         toolbar = modest_widget_factory_get_edit_toolbar (priv->factory, buttons);
352         g_slist_free (buttons);
353
354         g_signal_connect (G_OBJECT(toolbar), "button_clicked",
355                           G_CALLBACK(on_toolbar_button_clicked), self);
356         
357         return toolbar;
358 }
359
360
361 static void
362 init_window (ModestEditMsgWindow *obj)
363 {
364         GtkWidget *to_button, *cc_button, *bcc_button; 
365         GtkWidget *header_table;
366         GtkWidget *main_vbox;
367         
368         ModestEditMsgWindowPrivate *priv;
369         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
370
371         to_button     = gtk_button_new_with_label (_("To..."));
372         cc_button     = gtk_button_new_with_label (_("Cc..."));
373         bcc_button    = gtk_button_new_with_label (_("Bcc..."));
374
375         priv->from_field    = modest_widget_factory_get_combo_box (priv->factory,
376                                                                    MODEST_COMBO_BOX_TYPE_TRANSPORTS);
377         priv->to_field      = gtk_entry_new_with_max_length (80);
378         priv->cc_field      = gtk_entry_new_with_max_length (80);
379         priv->bcc_field     = gtk_entry_new_with_max_length (80);
380         priv->subject_field = gtk_entry_new_with_max_length (80);
381         
382         header_table = gtk_table_new (5,2, FALSE);
383         
384         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("From:")),
385                           0,1,0,1, GTK_SHRINK, 0, 0, 0);
386         gtk_table_attach (GTK_TABLE(header_table), to_button,     0,1,1,2, GTK_SHRINK, 0, 0, 0);
387         gtk_table_attach (GTK_TABLE(header_table), cc_button,     0,1,2,3, GTK_SHRINK, 0, 0, 0);
388         gtk_table_attach (GTK_TABLE(header_table), bcc_button,    0,1,3,4, GTK_SHRINK, 0, 0, 0);
389         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("Subject:")),
390                           0,1,4,5, GTK_SHRINK, 0, 0, 0);
391
392         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->from_field,   1,2,0,1);
393         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->to_field,     1,2,1,2);
394         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->cc_field,     1,2,2,3);
395         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->bcc_field,    1,2,3,4);
396         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->subject_field,1,2,4,5);
397
398         priv->msg_body = gtk_text_view_new ();
399         
400         main_vbox = gtk_vbox_new  (FALSE, 6);
401
402         priv->menubar = menubar_new (obj);
403         priv->toolbar = GTK_WIDGET(toolbar_new (obj));
404
405         gtk_box_pack_start (GTK_BOX(main_vbox), priv->menubar, FALSE, FALSE, 0);
406         gtk_box_pack_start (GTK_BOX(main_vbox), priv->toolbar, FALSE, FALSE, 0);
407         gtk_box_pack_start (GTK_BOX(main_vbox), header_table, FALSE, FALSE, 6);
408         gtk_box_pack_start (GTK_BOX(main_vbox), priv->msg_body, TRUE, TRUE, 6);
409
410         gtk_widget_show_all (GTK_WIDGET(main_vbox));
411         gtk_container_add (GTK_CONTAINER(obj), main_vbox);
412 }
413         
414
415
416 static void
417 modest_edit_msg_window_finalize (GObject *obj)
418 {
419         ModestEditMsgWindowPrivate *priv;
420
421         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
422
423         g_object_unref (G_OBJECT(priv->factory));
424         priv->factory = NULL;
425         
426         G_OBJECT_CLASS(parent_class)->finalize (obj);
427
428 }
429
430
431
432 static gboolean
433 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestEditMsgWindow *self)
434 {
435         save_settings (self);
436         return FALSE;
437 }
438
439
440 GtkWidget*
441 modest_edit_msg_window_new (ModestWidgetFactory *factory,
442                             ModestEditType type, TnyMsg *msg)
443 {
444         GObject *obj;
445         ModestEditMsgWindowPrivate *priv;
446
447         g_return_val_if_fail (factory, NULL);
448         g_return_val_if_fail (type < MODEST_EDIT_TYPE_NUM, NULL);
449         g_return_val_if_fail (!(type==MODEST_EDIT_TYPE_NEW && msg), NULL); 
450         g_return_val_if_fail (!(type!=MODEST_EDIT_TYPE_NEW && !msg), NULL);     
451         
452         obj = g_object_new(MODEST_TYPE_EDIT_MSG_WINDOW, NULL);
453         priv = MODEST_EDIT_MSG_WINDOW_GET_PRIVATE(obj);
454
455         g_object_ref (factory);
456         priv->factory = factory;
457
458         init_window (MODEST_EDIT_MSG_WINDOW(obj));
459
460         restore_settings (MODEST_EDIT_MSG_WINDOW(obj));
461         
462         gtk_window_set_title (GTK_WINDOW(obj), "Modest");
463         gtk_window_set_icon  (GTK_WINDOW(obj),
464                               modest_icon_factory_get_icon (MODEST_APP_ICON));
465
466         g_signal_connect (G_OBJECT(obj), "delete-event",
467                           G_CALLBACK(on_delete_event), obj);
468         
469         if (msg) {
470                 /* Testing code. Should be into a set_msg method */
471                 TnyHeader *header;
472                 GtkTextBuffer *buf;
473
474                 header = tny_msg_get_header (msg);
475                 gtk_entry_set_text (GTK_ENTRY(priv->to_field),
476                                     tny_header_get_to (header));
477                 gtk_entry_set_text (GTK_ENTRY(priv->cc_field),
478                                     tny_header_get_cc (header));
479                 gtk_entry_set_text (GTK_ENTRY(priv->bcc_field),
480                                     tny_header_get_bcc (header));
481                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field),
482                                     tny_header_get_subject (header));   
483
484                 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->msg_body));
485                 gtk_text_buffer_set_text (buf,
486                                           (const gchar *) modest_tny_msg_actions_find_body (msg, TRUE),
487                                           -1);
488
489                 /* TODO: lower priority, select in the From: combo to
490                    the value that comes from msg */
491
492                 /* TODO: set attachments */
493         }
494
495         return GTK_WIDGET (obj);
496 }