2007-05-07 Murray Cumming <murrayc@murrayc.com>
[modest] / src / gnome / modest-msg-edit-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 <string.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
33 #include <modest-conf.h>
34 #include <modest-runtime.h>
35 #include <modest-tny-msg.h>
36
37 #include <widgets/modest-window-priv.h>
38 #include <widgets/modest-msg-edit-window.h>
39 #include <widgets/modest-msg-edit-window-ui.h>
40 #include <widgets/modest-combo-box.h>
41
42 #include <modest-widget-memory.h>
43 #include <modest-account-mgr-helpers.h>
44
45 static void  modest_msg_edit_window_class_init   (ModestMsgEditWindowClass *klass);
46 static void  modest_msg_edit_window_init         (ModestMsgEditWindow *obj);
47 static void  modest_msg_edit_window_finalize     (GObject *obj);
48
49 /* list my signals */
50 enum {
51         /* MY_SIGNAL_1, */
52         /* MY_SIGNAL_2, */
53         LAST_SIGNAL
54 };
55
56 typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate;
57 struct _ModestMsgEditWindowPrivate {
58
59         GtkWidget   *toolbar;
60         GtkWidget   *menubar;
61
62         GtkWidget   *msg_body;
63         GtkWidget   *from_field;
64         GtkWidget   *to_field;
65         GtkWidget   *cc_field;
66         GtkWidget   *bcc_field;
67         GtkWidget   *subject_field;
68 };
69
70 #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
71                                                     MODEST_TYPE_MSG_EDIT_WINDOW, \
72                                                     ModestMsgEditWindowPrivate))
73 /* globals */
74 static GtkWindowClass *parent_class = NULL;
75
76 /* uncomment the following if you have defined any signals */
77 /* static guint signals[LAST_SIGNAL] = {0}; */
78
79 GType
80 modest_msg_edit_window_get_type (void)
81 {
82         static GType my_type = 0;
83         if (!my_type) {
84                 static const GTypeInfo my_info = {
85                         sizeof(ModestMsgEditWindowClass),
86                         NULL,           /* base init */
87                         NULL,           /* base finalize */
88                         (GClassInitFunc) modest_msg_edit_window_class_init,
89                         NULL,           /* class finalize */
90                         NULL,           /* class data */
91                         sizeof(ModestMsgEditWindow),
92                         1,              /* n_preallocs */
93                         (GInstanceInitFunc) modest_msg_edit_window_init,
94                         NULL
95                 };
96                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
97                                                   "ModestMsgEditWindow",
98                                                   &my_info, 0);
99         }
100         return my_type;
101 }
102
103
104 static void
105 save_state (ModestWindow *self)
106 {
107         modest_widget_memory_save (modest_runtime_get_conf (),
108                                     G_OBJECT(self), "modest-edit-msg-window");
109 }
110
111
112 static void
113 restore_settings (ModestMsgEditWindow *self)
114 {
115         modest_widget_memory_restore (modest_runtime_get_conf (),
116                                       G_OBJECT(self), "modest-edit-msg-window");
117 }
118
119 static void
120 modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass)
121 {
122         GObjectClass *gobject_class;
123         gobject_class = (GObjectClass*) klass;
124
125         parent_class            = g_type_class_peek_parent (klass);
126         gobject_class->finalize = modest_msg_edit_window_finalize;
127
128         g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate));
129
130         modest_window_class->save_state_func = save_state;
131 }
132
133 static void
134 modest_msg_edit_window_init (ModestMsgEditWindow *obj)
135 {
136         ModestMsgEditWindowPrivate *priv;
137         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
138
139         priv->toolbar       = NULL;
140         priv->menubar       = NULL;
141         priv->msg_body      = NULL;
142         priv->from_field    = NULL;
143         priv->to_field      = NULL;
144         priv->cc_field      = NULL;
145         priv->bcc_field     = NULL;
146         priv->subject_field = NULL;
147 }
148
149
150 static ModestPairList*
151 get_transports (void)
152 {
153         ModestAccountMgr *account_mgr;
154         GSList *transports = NULL;
155         GSList *cursor, *accounts;
156         
157         account_mgr = modest_runtime_get_account_mgr();
158         cursor = accounts = modest_account_mgr_account_names (account_mgr, TRUE);
159         while (cursor) {
160                 gchar *account_name = (gchar*)cursor->data;
161                 gchar *from_string  = modest_account_mgr_get_from_string (account_mgr,
162                                                                           account_name);
163                 if (!from_string)  {
164                         /* something went wrong: ignore this one */
165                         g_free (account_name);
166                         cursor->data = NULL;
167                 } else {
168                         ModestPair *pair;
169                         pair = modest_pair_new ((gpointer) account_name,
170                                                 (gpointer) from_string , TRUE);
171                         transports = g_slist_prepend (transports, pair);
172                 } /* don't free account name; it's freed when the transports list is freed */
173                 cursor = cursor->next;
174         }
175         g_slist_free (accounts);
176         return transports;
177 }
178
179
180 static void
181 on_from_combo_changed (ModestComboBox *combo, ModestWindow *win)
182 {
183         modest_window_set_active_account (
184                 win, modest_combo_box_get_active_id(combo));
185 }
186
187
188
189 static void
190 init_window (ModestMsgEditWindow *obj, const gchar* account)
191 {
192         GtkWidget *to_button, *cc_button, *bcc_button; 
193         GtkWidget *header_table;
194         GtkWidget *main_vbox;
195         ModestMsgEditWindowPrivate *priv;
196         ModestWindowPrivate *parent_priv;
197         ModestPairList *protos;
198         
199         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj);
200         parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
201
202         to_button     = gtk_button_new_with_label (_("To..."));
203         cc_button     = gtk_button_new_with_label (_("Cc..."));
204         bcc_button    = gtk_button_new_with_label (_("Bcc..."));
205         
206         protos = get_transports ();
207         priv->from_field    = modest_combo_box_new (protos, g_str_equal);
208         modest_pair_list_free (protos);
209         if (account) {
210                 modest_combo_box_set_active_id (MODEST_COMBO_BOX(priv->from_field),
211                                                 (gpointer)account);
212                 modest_window_set_active_account (MODEST_WINDOW(obj), account);
213         }
214         /* auto-update the active account */
215         g_signal_connect (G_OBJECT(priv->from_field), "changed", G_CALLBACK(on_from_combo_changed), obj);
216         
217         priv->to_field      = gtk_entry_new_with_max_length (80);
218         priv->cc_field      = gtk_entry_new_with_max_length (80);
219         priv->bcc_field     = gtk_entry_new_with_max_length (80);
220         priv->subject_field = gtk_entry_new_with_max_length (80);
221         
222         header_table = gtk_table_new (5,2, FALSE);
223         
224         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("From:")),
225                           0,1,0,1, GTK_SHRINK, 0, 0, 0);
226         gtk_table_attach (GTK_TABLE(header_table), to_button,     0,1,1,2, GTK_SHRINK, 0, 0, 0);
227         gtk_table_attach (GTK_TABLE(header_table), cc_button,     0,1,2,3, GTK_SHRINK, 0, 0, 0);
228         gtk_table_attach (GTK_TABLE(header_table), bcc_button,    0,1,3,4, GTK_SHRINK, 0, 0, 0);
229         gtk_table_attach (GTK_TABLE(header_table), gtk_label_new (_("Subject:")),
230                           0,1,4,5, GTK_SHRINK, 0, 0, 0);
231
232         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->from_field,   1,2,0,1);
233         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->to_field,     1,2,1,2);
234         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->cc_field,     1,2,2,3);
235         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->bcc_field,    1,2,3,4);
236         gtk_table_attach_defaults (GTK_TABLE(header_table), priv->subject_field,1,2,4,5);
237
238         priv->msg_body = gtk_text_view_new ();
239         
240         main_vbox = gtk_vbox_new  (FALSE, 6);
241
242         gtk_box_pack_start (GTK_BOX(main_vbox), priv->menubar, FALSE, FALSE, 0);
243         gtk_box_pack_start (GTK_BOX(main_vbox), priv->toolbar, FALSE, FALSE, 0);
244         gtk_box_pack_start (GTK_BOX(main_vbox), header_table, FALSE, FALSE, 6);
245         gtk_box_pack_start (GTK_BOX(main_vbox), priv->msg_body, TRUE, TRUE, 6);
246
247         gtk_widget_show_all (GTK_WIDGET(main_vbox));
248         gtk_container_add (GTK_CONTAINER(obj), main_vbox);
249 }
250
251
252 static void
253 modest_msg_edit_window_finalize (GObject *obj)
254 {
255         G_OBJECT_CLASS(parent_class)->finalize (obj);
256 }
257
258
259
260 static gboolean
261 on_delete_event (GtkWidget *widget, GdkEvent *event, ModestMsgEditWindow *self)
262 {
263         modest_window_save_state (MODEST_WINDOW(self));
264         return FALSE;
265 }
266
267
268 static void
269 set_msg (ModestMsgEditWindow *self, TnyMsg *msg)
270 {
271         TnyHeader *header;
272         GtkTextBuffer *buf;
273         const gchar *to, *cc, *bcc, *subject;
274         ModestMsgEditWindowPrivate *priv;
275         gchar *body;
276         
277         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self));
278         g_return_if_fail (TNY_IS_MSG (msg));
279
280         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self);
281
282         header  = tny_msg_get_header (msg);
283         to      = tny_header_get_to (header);
284         cc      = tny_header_get_cc (header);
285         bcc     = tny_header_get_bcc (header);
286         subject = tny_header_get_subject (header);
287
288         if (to)
289                 gtk_entry_set_text (GTK_ENTRY(priv->to_field), to);
290         if (cc)
291                 gtk_entry_set_text (GTK_ENTRY(priv->cc_field), cc);
292         if (bcc)
293                 gtk_entry_set_text (GTK_ENTRY(priv->bcc_field),  bcc);
294         if (subject)
295                 gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject);
296
297         
298         buf  = gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->msg_body));
299         body = modest_tny_msg_get_body (msg, FALSE);
300         if (body) 
301                 gtk_text_buffer_set_text (buf, body, -1);
302         g_free (body);
303 }
304
305
306 ModestWindow *
307 modest_msg_edit_window_new (TnyMsg *msg, const gchar *account)
308 {
309         ModestMsgEditWindow *self;
310         ModestMsgEditWindowPrivate *priv;
311         ModestWindowPrivate *parent_priv;
312         GtkActionGroup *action_group;
313         GError *error = NULL;
314
315         g_return_val_if_fail (msg, NULL);
316         
317         self = MODEST_MSG_EDIT_WINDOW(g_object_new(MODEST_TYPE_MSG_EDIT_WINDOW, NULL));
318         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(self);
319         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
320         
321         parent_priv->ui_manager = gtk_ui_manager_new();
322         action_group = gtk_action_group_new ("ModestMsgEditWindowActions");
323
324         /* Add common actions */
325         gtk_action_group_add_actions (action_group,
326                                       modest_msg_edit_action_entries,
327                                       G_N_ELEMENTS (modest_msg_edit_action_entries),
328                                       self);
329         gtk_action_group_add_toggle_actions (action_group,
330                                              modest_msg_edit_toggle_action_entries,
331                                              G_N_ELEMENTS (modest_msg_edit_toggle_action_entries),
332                                              self);
333         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
334         g_object_unref (action_group);
335
336         /* Load the UI definition */
337         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager,
338                                          MODEST_UIDIR "modest-msg-edit-window-ui.xml",
339                                          &error);
340         if (error) {
341                 g_printerr ("modest: could not merge modest-msg-edit-window-ui.xml: %s\n", error->message);
342                 g_error_free (error);
343                 error = NULL;
344         }
345         /* ****** */
346
347         /* Add accelerators */
348         gtk_window_add_accel_group (GTK_WINDOW (self), 
349                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
350
351         /* Toolbar / Menubar */
352         priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar");
353         priv->menubar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar");
354
355         gtk_toolbar_set_tooltips (GTK_TOOLBAR (priv->toolbar), TRUE);
356
357         /* Init window */
358         init_window (MODEST_MSG_EDIT_WINDOW(self), account);
359
360         restore_settings (MODEST_MSG_EDIT_WINDOW(self));
361         
362         gtk_window_set_title (GTK_WINDOW(self), "Modest");
363         gtk_window_set_icon_from_file (GTK_WINDOW(self), MODEST_APP_ICON, NULL);
364
365         g_signal_connect (G_OBJECT(self), "delete-event",
366                           G_CALLBACK(on_delete_event), self);
367         
368         set_msg (self, msg);
369         
370         return MODEST_WINDOW(self);
371 }
372
373
374 MsgData * 
375 modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window)
376 {
377         MsgData *data;
378         GtkTextBuffer *buf;
379         GtkTextIter b, e;
380         const gchar *account_name;
381         gchar *from_string = NULL;
382         ModestMsgEditWindowPrivate *priv;
383         
384         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL);
385
386         priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window);
387         
388         account_name = (gchar*)modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field));
389         if (account_name) 
390                 from_string = modest_account_mgr_get_from_string (
391                         modest_runtime_get_account_mgr(), account_name);
392         if (!from_string) {
393                 g_printerr ("modest: cannot get from string\n");
394                 return NULL;
395         }
396         
397         buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body));
398         gtk_text_buffer_get_bounds (buf, &b, &e);
399
400         /* don't free these (except from) */
401         data = g_slice_new0 (MsgData);
402         data->from    =  from_string, /* will be freed when data is freed */
403         data->to      =  (gchar*) gtk_entry_get_text (GTK_ENTRY(priv->to_field));
404         data->cc      =  (gchar*) gtk_entry_get_text (GTK_ENTRY(priv->cc_field));
405         data->bcc     =  (gchar*) gtk_entry_get_text (GTK_ENTRY(priv->bcc_field));
406         data->subject =  (gchar*) gtk_entry_get_text (GTK_ENTRY(priv->subject_field));
407         data->plain_body    =  gtk_text_buffer_get_text (buf, &b, &e, FALSE);
408         /* No rich supported yet, then html body is NULL */
409         data->html_body = NULL;
410
411         return data;
412 }
413
414 void 
415 modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window,
416                                       MsgData *data)
417 {
418         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window));
419
420         g_free (data->from);
421         g_free (data->plain_body);
422         g_free (data->html_body);
423         g_slice_free (MsgData, data);
424 }
425
426 /* Rich formatting API functions */
427 ModestMsgEditFormat
428 modest_msg_edit_window_get_format (ModestMsgEditWindow *self)
429 {
430         return MODEST_MSG_EDIT_FORMAT_TEXT;
431 }
432
433 void
434 modest_msg_edit_window_set_format (ModestMsgEditWindow *self,
435                                    ModestMsgEditFormat format)
436 {
437         switch (format) {
438         case MODEST_MSG_EDIT_FORMAT_TEXT:
439                 break;
440         case MODEST_MSG_EDIT_FORMAT_HTML:
441                 g_message ("HTML format not supported in Gnome ModestMsgEditWindow");
442                 break;
443         default:
444                 break;
445         }
446 }
447
448 ModestMsgEditFormatState *
449 modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self)
450 {
451         ModestMsgEditFormatState *format_state;
452
453         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL);
454
455         format_state = g_new0 (ModestMsgEditFormatState, 1);
456
457         return format_state;
458 }
459
460 void
461 modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self, 
462                                          const ModestMsgEditFormatState *format_state)
463 {
464         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (self));
465
466         /* Ends silently as set_format_state should do nothing when edit window format
467            is not HTML */
468         return;
469 }
470
471 void
472 modest_msg_edit_window_select_color (ModestMsgEditWindow *window)
473 {
474         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
475
476         g_message ("Select color operation is not supported");
477 }
478
479 void
480 modest_msg_edit_window_select_file_format (ModestMsgEditWindow *window,
481                                            gint file_format)
482 {
483         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
484
485         g_message ("Select file format operation is not supported");
486 }
487
488 void
489 modest_msg_edit_window_select_font (ModestMsgEditWindow *window)
490 {
491         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
492
493         g_message ("Select font operation is not supported");
494 }
495
496 void
497 modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window)
498 {
499         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
500
501         g_message ("Select background color operation is not supported");
502 }
503
504 void
505 modest_msg_edit_window_insert_image (ModestMsgEditWindow *window)
506 {
507         g_return_if_fail (MODEST_MSG_EDIT_WINDOW (window));
508
509         g_message ("Insert image operation is not supported");
510 }
511
512 void
513 modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, 
514                                 gboolean show)
515 {
516         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
517
518         g_message ("not implemented yet %s", __FUNCTION__);
519 }
520 void
521 modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, 
522                                 gboolean show)
523 {
524         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
525
526         g_message ("not implemented yet %s", __FUNCTION__);
527 }
528 void
529 modest_msg_edit_window_undo (ModestMsgEditWindow *window)
530 {
531         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
532       
533         g_message ("not implemented yet %s", __FUNCTION__);
534 }
535 void
536 modest_msg_edit_window_toggle_fullscreen (ModestMsgEditWindow *window)
537 {
538         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
539       
540         g_message ("not implemented yet %s", __FUNCTION__);
541 }
542 void
543 modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window,
544                                            TnyHeaderFlags priority_flags)
545 {
546         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
547       
548         g_message ("not implemented yet %s", __FUNCTION__);
549 }
550
551
552 void
553 modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window)
554 {
555         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
556
557         g_message ("not implemented yet %s", __FUNCTION__);
558 }
559
560 gboolean
561 modest_msg_edit_window_check_names (ModestMsgEditWindow *window)
562 {
563         g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE);
564
565         g_message ("not implemented yet %s", __FUNCTION__);
566         return TRUE;
567 }
568
569 void
570 modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window,
571                                         gint file_format)
572 {
573         g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window));
574
575         g_message ("not implemented yet %s", __FUNCTION__);
576 }