* Cleaned some header files, deleting unused includes
[modest] / src / modest-ui.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 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif /*HAVE_CONFIG_H*/
33
34 #include <glib/gi18n.h>
35 #include "modest-ui-priv.h"
36 #include "modest-ui.h"
37 #include "modest-ui-actions.h"
38 #include "modest-icon-names.h"
39 #include "modest-tny-platform-factory.h"
40 #include "modest-account-view-window.h"
41 #include "modest-main-window.h"
42 #include <tny-error.h>
43 #include <tny-simple-list.h>
44
45 #define MODEST_UI_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
46                                        MODEST_TYPE_UI, \
47                                        ModestUIPrivate))
48
49 typedef struct _GetMsgAsyncHelper {
50         ModestMainWindow *main_window;
51         TnyIterator *iter;
52         GFunc func;
53         gpointer user_data;
54 } GetMsgAsyncHelper;
55
56 typedef enum _ReplyForwardAction {
57         ACTION_REPLY,
58         ACTION_REPLY_TO_ALL,
59         ACTION_FORWARD
60 } ReplyForwardAction;
61
62 typedef struct _ReplyForwardHelper {
63         guint reply_forward_type;
64         ReplyForwardAction action;
65         gchar *from;
66 } ReplyForwardHelper;
67
68 /* globals */
69 static GObjectClass *parent_class = NULL;
70
71 /* 'private'/'protected' functions */
72 static void   modest_ui_class_init     (ModestUIClass *klass);
73 static void   modest_ui_init           (ModestUI *obj);
74 static void   modest_ui_finalize       (GObject *obj);
75
76 static void   register_stock_icons        ();
77 static void   connect_signals             (ModestUI *self);
78 /* static GtkUIManager *create_ui_manager (); */
79
80 static void   reply_forward_func (gpointer data, gpointer user_data);
81 static void   read_msg_func      (gpointer data, gpointer user_data);
82 static void   get_msg_cb         (TnyFolder *folder, 
83                                   TnyMsg *msg, 
84                                   GError **err, 
85                                   gpointer user_data);
86
87 static void   reply_forward      (GtkWidget *widget,
88                                   ReplyForwardAction action,
89                                   ModestMainWindow *main_window);
90
91 static void  _modest_ui_actions_on_password_requested   (ModestTnyAccountStore *account_store, 
92                                                          const gchar* account_name,
93                                                          gchar **password, 
94                                                          gboolean *cancel, 
95                                                          gboolean *remember, 
96                                                          ModestMainWindow *main_window);
97
98 GType
99 modest_ui_get_type (void)
100 {
101         static GType my_type = 0;
102         if (!my_type) {
103                 static const GTypeInfo my_info = {
104                         sizeof(ModestUIClass),
105                         NULL,           /* base init */
106                         NULL,           /* base finalize */
107                         (GClassInitFunc) modest_ui_class_init,
108                         NULL,           /* class finalize */
109                         NULL,           /* class data */
110                         sizeof(ModestUI),
111                         1,              /* n_preallocs */
112                         (GInstanceInitFunc) modest_ui_init,
113                         NULL
114                 };
115                 my_type = g_type_register_static (G_TYPE_OBJECT,
116                                                   "ModestUI",
117                                                   &my_info, 0);
118         }
119         return my_type;
120 }
121
122
123 static void
124 modest_ui_class_init (ModestUIClass *klass)
125 {
126         GObjectClass *gobject_class;
127         gobject_class = (GObjectClass*) klass;
128
129         parent_class            = g_type_class_peek_parent (klass);
130         gobject_class->finalize = modest_ui_finalize;
131
132         g_type_class_add_private (gobject_class, sizeof(ModestUIPrivate));
133
134 }
135
136
137 static void
138 modest_ui_init (ModestUI *obj)
139 {
140         ModestUIPrivate *priv;
141
142         priv = MODEST_UI_GET_PRIVATE(obj);
143
144         priv->widget_factory = NULL;
145         priv->main_window    = NULL;
146         priv->account_store  = NULL;
147 }
148
149
150 static void
151 modest_ui_finalize (GObject *obj)
152 {
153         
154         ModestUIPrivate *priv = MODEST_UI_GET_PRIVATE(obj);
155         
156         if (priv->widget_factory) {
157                 g_object_unref (G_OBJECT(priv->widget_factory));
158                 priv->widget_factory = NULL;
159         }
160
161         if (priv->ui_manager) {
162                 g_object_unref (G_OBJECT(priv->ui_manager));
163                 priv->ui_manager = NULL;
164         }
165         
166         G_OBJECT_CLASS(parent_class)->finalize (obj);
167 }
168
169
170 ModestUI*
171 modest_ui_new (TnyAccountStore *account_store)
172 {
173         GObject *obj;
174         ModestUIPrivate *priv;
175         TnyPlatformFactory *fact;
176         ModestAccountMgr *account_mgr;
177
178         obj  = g_object_new(MODEST_TYPE_UI, NULL);
179         priv = MODEST_UI_GET_PRIVATE(obj);
180
181         /* Get the platform-dependent instances */
182         fact = modest_tny_platform_factory_get_instance ();
183         
184         priv->account_store = account_store;
185
186         account_mgr = modest_tny_platform_factory_get_modest_account_mgr_instance (fact);
187         if (!account_mgr) {
188                 g_printerr ("modest: could not create ModestAccountMgr instance\n");
189                 g_object_unref (obj);
190                 return NULL;
191         }
192
193         priv->widget_factory = modest_widget_factory_new ();
194         if (!priv->widget_factory) {
195                 g_printerr ("modest: could not initialize widget factory\n");
196                 return NULL;
197         }
198
199         /* Register our own icons as stock icons in order to
200            use them with the UI manager */
201         register_stock_icons ();
202                 
203         return MODEST_UI(obj);
204 }
205
206 static gboolean
207 on_main_window_destroy (GtkObject *widget, ModestUI *self)
208 {
209         /* FIXME: check if there any viewer/editing windows opened */
210         gtk_main_quit ();
211         return FALSE;
212 }
213
214
215 ModestWindow *
216 modest_ui_main_window (ModestUI *self)
217 {
218         ModestUIPrivate *priv;
219
220         g_return_val_if_fail (self, NULL);
221         priv = MODEST_UI_GET_PRIVATE(self);
222
223         if (!priv->main_window) {
224
225                 /* Create main window */
226                 priv->main_window = modest_main_window_new (priv->widget_factory,
227                                                             priv->account_store);
228
229                 connect_signals (self);
230         }
231                 
232         if (!priv->main_window)
233                 g_printerr ("modest: could not create main window\n");
234         
235         return priv->main_window;
236 }
237
238 ModestWindow *
239 modest_ui_edit_window (ModestUI *self, ModestEditType edit_type)
240 {
241         ModestUIPrivate *priv;
242         ModestWindow *edit_window;
243
244         g_return_val_if_fail (self, NULL);
245         priv = MODEST_UI_GET_PRIVATE(self);
246
247         /* Create window */
248         edit_window = modest_edit_msg_window_new (priv->widget_factory, 
249                                                   priv->account_store,
250                                                   edit_type);
251         
252         /* Connect Edit Window signals */
253 /*      connect_edit_window_signals (self); */
254                 
255         return edit_window;
256 }
257
258 /* 
259  *  This function registers our custom toolbar icons, so they can be
260  *  themed. The idea of this function was taken from the gtk-demo
261  */
262 static void
263 register_stock_icons ()
264 {
265         static gboolean registered = FALSE;
266   
267         if (!registered) {
268                 GdkPixbuf *pixbuf;
269                 GtkIconFactory *factory;
270                 gchar *filename;
271                 gint i;
272
273                 static GtkStockItem items[] = {
274                         { MODEST_STOCK_MAIL_SEND, "send mail", 0, 0, NULL },
275                         { MODEST_STOCK_NEW_MAIL, "new mail", 0, 0, NULL },
276                         { MODEST_STOCK_SEND_RECEIVE, "send receive", 0, 0, NULL },
277                         { MODEST_STOCK_REPLY, "reply", 0, 0, NULL },
278                         { MODEST_STOCK_REPLY_ALL, "reply all", 0, 0, NULL },
279                         { MODEST_STOCK_FORWARD, "forward", 0, 0, NULL },
280                         { MODEST_STOCK_DELETE, "delete", 0, 0, NULL },
281                         { MODEST_STOCK_NEXT, "next", 0, 0, NULL },
282                         { MODEST_STOCK_PREV, "prev", 0, 0, NULL },
283                         { MODEST_STOCK_STOP, "stop", 0, 0, NULL }
284                 };
285       
286                 static gchar *items_names [] = {
287                         MODEST_TOOLBAR_ICON_MAIL_SEND,
288                         MODEST_TOOLBAR_ICON_NEW_MAIL,           
289                         MODEST_TOOLBAR_ICON_SEND_RECEIVE,
290                         MODEST_TOOLBAR_ICON_REPLY,      
291                         MODEST_TOOLBAR_ICON_REPLY_ALL,
292                         MODEST_TOOLBAR_ICON_FORWARD,
293                         MODEST_TOOLBAR_ICON_DELETE,
294                         MODEST_TOOLBAR_ICON_NEXT,
295                         MODEST_TOOLBAR_ICON_PREV,
296                         MODEST_TOOLBAR_ICON_STOP
297                 };
298
299                 registered = TRUE;
300
301                 /* Register our stock items */
302                 gtk_stock_add (items, G_N_ELEMENTS (items));
303       
304                 /* Add our custom icon factory to the list of defaults */
305                 factory = gtk_icon_factory_new ();
306                 gtk_icon_factory_add_default (factory);
307
308                 /* Register icons to accompany stock items */
309                 for (i = 0; i < G_N_ELEMENTS (items); i++) {
310                         pixbuf = NULL;
311                         pixbuf = gdk_pixbuf_new_from_file (items_names[i], NULL);
312
313                         if (pixbuf != NULL) {
314                                 GtkIconSet *icon_set;
315                                 GdkPixbuf *transparent;
316
317                                 transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
318
319                                 icon_set = gtk_icon_set_new_from_pixbuf (transparent);
320                                 gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
321                                 gtk_icon_set_unref (icon_set);
322                                 g_object_unref (pixbuf);
323                                 g_object_unref (transparent);
324                         }
325                         else
326                                 g_warning ("failed to load %s icon", items_names[i]);
327                 }
328                 /* Drop our reference to the factory, GTK will hold a reference. */
329                 g_object_unref (factory);
330         }
331 }
332
333 /* FIXME: uninit these as well */
334 static void
335 connect_signals (ModestUI *self)
336 {
337         TnyDevice *device;
338         ModestUIPrivate *priv;
339         ModestFolderView *folder_view;
340         ModestHeaderView *header_view;
341         ModestMsgView *msg_view;
342         GtkWidget *toggle;
343         
344         priv = MODEST_UI_GET_PRIVATE(self);
345
346         folder_view = modest_widget_factory_get_folder_view (priv->widget_factory);
347         header_view = modest_widget_factory_get_header_view (priv->widget_factory);
348         msg_view    = modest_widget_factory_get_msg_preview (priv->widget_factory);
349         toggle      = modest_widget_factory_get_online_toggle (priv->widget_factory);
350         device      = tny_account_store_get_device (priv->account_store);
351
352         /* folder view */
353         g_signal_connect (G_OBJECT(folder_view), "folder_selection_changed",
354                           G_CALLBACK(_modest_ui_actions_on_folder_selection_changed),
355                           priv->main_window);
356 /*      g_signal_connect (G_OBJECT(folder_view), "key-press-event", */
357 /*                        G_CALLBACK(on_folder_key_press_event), priv->widget_factory); */
358
359         /* header view */
360         g_signal_connect (G_OBJECT(header_view), "status_update",
361                           G_CALLBACK(_modest_ui_actions_on_header_status_update), 
362                           priv->main_window);
363         g_signal_connect (G_OBJECT(header_view), "header_selected",
364                           G_CALLBACK(_modest_ui_actions_on_header_selected), 
365                           priv->main_window);
366         g_signal_connect (G_OBJECT(header_view), "item_not_found",
367                           G_CALLBACK(_modest_ui_actions_on_item_not_found), 
368                           priv->main_window);
369
370         
371         /* msg preview */
372         g_signal_connect (G_OBJECT(msg_view), "link_clicked",
373                           G_CALLBACK(_modest_ui_actions_on_msg_link_clicked), 
374                           priv->main_window);
375         g_signal_connect (G_OBJECT(msg_view), "link_hover",
376                           G_CALLBACK(_modest_ui_actions_on_msg_link_hover), 
377                           priv->main_window);
378         g_signal_connect (G_OBJECT(msg_view), "attachment_clicked",
379                           G_CALLBACK(_modest_ui_actions_on_msg_attachment_clicked), 
380                           priv->main_window);
381
382         /* Device */
383         g_signal_connect (G_OBJECT(device), "connection_changed",
384                           G_CALLBACK(_modest_ui_actions_on_connection_changed), 
385                           priv->main_window);
386         g_signal_connect (G_OBJECT(toggle), "toggled",
387                           G_CALLBACK(_modest_ui_actions_on_online_toggle_toggled),
388                           priv->main_window);
389                 
390         /* account store */
391         g_signal_connect (G_OBJECT (priv->account_store), 
392                           "password_requested",
393                           G_CALLBACK(_modest_ui_actions_on_password_requested),
394                           priv->main_window);
395
396         /* Destroy window */
397         g_signal_connect (G_OBJECT(priv->main_window), 
398                           "destroy",
399                           G_CALLBACK(on_main_window_destroy), 
400                           NULL);
401
402
403         /* Init toggle in correct state */
404         _modest_ui_actions_on_connection_changed (device,
405                                                  tny_device_is_online (device),
406                                                  MODEST_MAIN_WINDOW (priv->main_window));
407 }
408
409
410 /* ***************************************************************** */
411 /*                M O D E S T    U I    A C T I O N S                */
412 /* ***************************************************************** */
413 void     
414 _modest_ui_actions_on_about (GtkWidget *widget, 
415                              ModestMainWindow *main_window)
416 {
417         GtkWidget *about;
418         const gchar *authors[] = {
419                 "Dirk-Jan C. Binnema <dirk-jan.binnema@nokia.com>",
420                 NULL
421         };
422         about = gtk_about_dialog_new ();
423         gtk_about_dialog_set_name (GTK_ABOUT_DIALOG(about), PACKAGE_NAME);
424         gtk_about_dialog_set_version (GTK_ABOUT_DIALOG(about),PACKAGE_VERSION);
425         gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG(about),
426                                         _("Copyright (c) 2006, Nokia Corporation\n"
427                                           "All rights reserved."));
428         gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG(about),
429                                        _("a modest e-mail client\n\n"
430                                          "design and implementation: Dirk-Jan C. Binnema\n"
431                                          "contributions from the fine people at KernelConcepts and Igalia\n"
432                                          "uses the tinymail email framework written by Philip van Hoof"));
433         gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG(about), authors);
434         gtk_about_dialog_set_website (GTK_ABOUT_DIALOG(about), "http://modest.garage.maemo.org");
435
436         gtk_dialog_run (GTK_DIALOG (about));
437         gtk_widget_destroy(about);
438 }
439
440 void
441 _modest_ui_actions_on_delete (GtkWidget *widget, 
442                              ModestMainWindow *main_window)
443 {
444         ModestWidgetFactory *widget_factory;
445         ModestHeaderView *header_view;
446         TnyList *header_list;
447         TnyIterator *iter;
448         GtkTreeModel *model;
449
450         widget_factory = modest_main_window_get_widget_factory (main_window);
451         header_view = modest_widget_factory_get_header_view (widget_factory);
452         header_list = modest_header_view_get_selected_headers (header_view);
453         g_object_unref (G_OBJECT(widget_factory));
454         
455         if (header_list) {
456                 iter = tny_list_create_iterator (header_list);
457                 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
458                 if (GTK_IS_TREE_MODEL_SORT (model))
459                         model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
460                 do {
461                         TnyHeader *header;
462                         ModestMailOperation *mail_op;
463
464                         header = TNY_HEADER (tny_iterator_get_current (iter));
465                         /* TODO: thick grain mail operation involving
466                            a list of objects. Composite pattern ??? */
467                         mail_op = modest_mail_operation_new ();
468
469                         /* TODO: add confirmation dialog */
470
471                         /* Move to trash */
472                         modest_mail_operation_remove_msg (mail_op, header, TRUE);
473
474                         /* Remove from tree model */
475                         if (modest_mail_operation_get_status (mail_op) == 
476                             MODEST_MAIL_OPERATION_STATUS_SUCCESS)
477                                 tny_list_remove (TNY_LIST (model), G_OBJECT (header));
478                         else {
479                                 /* TODO: error handling management */
480                                 const GError *error;
481                                 error = modest_mail_operation_get_error (mail_op);
482                                 g_warning (error->message);
483                         }
484
485                         g_object_unref (G_OBJECT (mail_op));
486                         g_object_unref (header);
487                         tny_iterator_next (iter);
488
489                 } while (!tny_iterator_is_done (iter));
490         }
491 }
492
493 void
494 _modest_ui_actions_on_quit (GtkWidget *widget, 
495                            ModestMainWindow *main_window)
496 {
497         /* FIXME: save size of main window */
498 /*      save_sizes (main_window); */
499         gtk_widget_destroy (GTK_WIDGET (main_window));
500 }
501
502 void
503 _modest_ui_actions_on_accounts (GtkWidget *widget, 
504                                ModestMainWindow *main_window)
505 {
506         GtkWidget *account_win;
507         ModestWidgetFactory *widget_factory;
508
509         widget_factory = modest_main_window_get_widget_factory (main_window);
510         account_win = modest_account_view_window_new (widget_factory);
511         g_object_unref (G_OBJECT(widget_factory));
512
513         gtk_window_set_transient_for (GTK_WINDOW (account_win),
514                                       GTK_WINDOW (main_window));
515                                       
516         gtk_widget_show (account_win);
517 }
518
519 void
520 _modest_ui_actions_on_new_msg (GtkWidget *widget, 
521                                ModestMainWindow *main_window)
522 {
523         ModestWindow *msg_win;
524         ModestWidgetFactory *widget_factory;
525         TnyAccountStore *account_store;
526
527         widget_factory = modest_main_window_get_widget_factory (main_window);
528         account_store = modest_main_window_get_account_store (main_window);
529         msg_win = modest_edit_msg_window_new (widget_factory, 
530                                               account_store,
531                                               MODEST_EDIT_TYPE_NEW);
532         g_object_unref (G_OBJECT (widget_factory));
533         g_object_unref (G_OBJECT (account_store));
534
535         gtk_widget_show (GTK_WIDGET (msg_win));
536 }
537
538 static void
539 reply_forward_func (gpointer data, gpointer user_data)
540 {
541         ModestWidgetFactory *widget_factory;
542         TnyHeader *new_header;
543         TnyMsg *msg, *new_msg;
544         TnyAccountStore *account_store;
545         GetMsgAsyncHelper *helper;
546         ReplyForwardHelper *rf_helper;
547         GtkWindow *msg_win;
548         ModestEditType edit_type;
549
550         msg = TNY_MSG (data);
551         helper = (GetMsgAsyncHelper *) user_data;
552         rf_helper = (ReplyForwardHelper *) helper->user_data;
553
554         /* Create reply mail */
555         switch (rf_helper->action) {
556         case ACTION_REPLY:
557                 new_msg = 
558                         modest_mail_operation_create_reply_mail (msg, 
559                                                                  rf_helper->from, 
560                                                                  rf_helper->reply_forward_type,
561                                                                  MODEST_MAIL_OPERATION_REPLY_MODE_SENDER);
562         case ACTION_REPLY_TO_ALL:
563                 new_msg = 
564                         modest_mail_operation_create_reply_mail (msg, rf_helper->from, rf_helper->reply_forward_type,
565                                                                  MODEST_MAIL_OPERATION_REPLY_MODE_ALL);
566                 edit_type = MODEST_EDIT_TYPE_REPLY;
567                 break;
568         case ACTION_FORWARD:
569                 new_msg = 
570                         modest_mail_operation_create_forward_mail (msg, rf_helper->from, rf_helper->reply_forward_type);
571                 edit_type = MODEST_EDIT_TYPE_FORWARD;
572                 break;
573         }
574
575         /* Set from */
576         new_header = tny_msg_get_header (new_msg);
577         tny_header_set_from (new_header, rf_helper->from);
578         g_object_unref (G_OBJECT (new_header));
579                 
580         /* Show edit window */
581         widget_factory = modest_main_window_get_widget_factory (helper->main_window);
582         account_store = modest_main_window_get_account_store (helper->main_window);
583         msg_win = modest_edit_msg_window_new (widget_factory, 
584                                               account_store,
585                                               MODEST_EDIT_TYPE_NEW);
586         g_object_unref (G_OBJECT (widget_factory));
587         g_object_unref (G_OBJECT (account_store));
588         modest_edit_msg_window_set_msg (MODEST_EDIT_MSG_WINDOW (msg_win),
589                                         new_msg);
590         gtk_widget_show (GTK_WIDGET (msg_win));
591         
592         /* Clean */
593         g_object_unref (G_OBJECT (new_msg));
594         g_free (rf_helper->from);
595         g_slice_free (ReplyForwardHelper, rf_helper);
596 }
597
598 /*
599  * Common code for the reply and forward actions
600  */
601 static void
602 reply_forward (GtkWidget *widget,
603                ReplyForwardAction action,
604                ModestMainWindow *main_window)
605 {
606         ModestHeaderView *header_view;
607         ModestWidgetFactory *widget_factory;
608         TnyList *header_list;
609         guint reply_forward_type;
610         ModestConf *conf;
611         TnyPlatformFactory *plat_factory;
612         TnyHeader *header;
613         TnyFolder *folder;
614         gchar *from, *key;
615         ModestFolderView *folder_view;
616         GetMsgAsyncHelper *helper;
617         ReplyForwardHelper *rf_helper;
618
619         /* Get ModestConf */
620         plat_factory = modest_tny_platform_factory_get_instance ();
621         conf = modest_tny_platform_factory_get_modest_conf_instance (plat_factory);
622
623         /* Get reply or forward type */
624         key = g_strdup_printf ("%s/%s", MODEST_CONF_NAMESPACE, 
625                                (action == ACTION_FORWARD) ? MODEST_CONF_FORWARD_TYPE : MODEST_CONF_REPLY_TYPE);
626         reply_forward_type = modest_conf_get_int (conf, key, NULL);
627         g_free (key);
628
629         /* Get the list of headers */
630         widget_factory = modest_main_window_get_widget_factory (main_window);
631         header_view = modest_widget_factory_get_header_view (widget_factory);
632         header_list = modest_header_view_get_selected_headers (header_view);
633         g_object_unref (G_OBJECT(widget_factory));
634
635         if (!header_list)
636                 return;
637
638         /* We assume that we can only select messages of the
639            same folder and that we reply all of them from the
640            same account. In fact the interface currently only
641            allows single selection */
642
643         /* TODO: get the from string from account */
644         from = g_strdup ("Invalid");
645         
646         /* Fill helpers */
647         rf_helper = g_slice_new0 (ReplyForwardHelper);
648         rf_helper->reply_forward_type = reply_forward_type;
649         rf_helper->action = action;
650         rf_helper->from = from;
651         
652         helper = g_slice_new0 (GetMsgAsyncHelper);
653         helper->main_window = main_window;
654         helper->func = reply_forward_func;
655         helper->iter = tny_list_create_iterator (header_list);
656         helper->user_data = rf_helper;
657         
658         header = TNY_HEADER (tny_iterator_get_current (helper->iter));
659         folder = tny_header_get_folder (header);
660         
661         /* The callback will call it per each header */
662         tny_folder_get_msg_async (folder, header, get_msg_cb, helper);
663         
664         /* Clean */
665         g_object_unref (G_OBJECT (folder));
666 }
667
668 void
669 _modest_ui_actions_on_reply (GtkWidget *widget,
670                             ModestMainWindow *main_window)
671 {
672         reply_forward (widget, ACTION_REPLY, main_window);
673 }
674
675 void
676 _modest_ui_actions_on_forward (GtkWidget *widget,
677                               ModestMainWindow *main_window)
678 {
679         reply_forward (widget, ACTION_FORWARD, main_window);
680 }
681
682 void
683 _modest_ui_actions_on_reply_all (GtkWidget *widget,
684                                 ModestMainWindow *main_window)
685 {
686         reply_forward (widget, ACTION_REPLY_TO_ALL, main_window);
687 }
688
689 void 
690 _modest_ui_actions_on_next (GtkWidget *widget, 
691                            ModestMainWindow *main_window)
692 {
693         ModestHeaderView *header_view;
694         ModestWidgetFactory *widget_factory;
695
696         widget_factory = modest_main_window_get_widget_factory (main_window);
697         header_view = modest_widget_factory_get_header_view (widget_factory);
698         g_object_unref (G_OBJECT(widget_factory));
699
700         modest_header_view_select_next (header_view);
701 }
702
703
704 /*
705  * Marks a message as read and passes it to the msg preview widget
706  */
707 static void
708 read_msg_func (gpointer data, gpointer user_data)
709 {
710         ModestMsgView *msg_view;
711         ModestWidgetFactory *widget_factory;
712         TnyMsg *msg;
713         TnyHeader *header;
714         GetMsgAsyncHelper *helper;
715         TnyHeaderFlags header_flags;
716
717         msg = TNY_MSG (data);
718         helper = (GetMsgAsyncHelper *) user_data;
719
720         /* mark message as seen; _set_flags crashes, bug in tinymail? */
721         header = TNY_HEADER (tny_iterator_get_current (helper->iter));
722         header_flags = tny_header_get_flags (header);
723         tny_header_set_flags (header, header_flags | TNY_HEADER_FLAG_SEEN);
724         g_object_unref (G_OBJECT (header));
725
726         /* Set message on msg view */
727         widget_factory = modest_main_window_get_widget_factory (helper->main_window);
728         msg_view = modest_widget_factory_get_msg_preview (widget_factory);
729         g_object_unref (G_OBJECT(widget_factory));
730         modest_msg_view_set_message (msg_view, msg);
731 }
732
733 /*
734  * This function is a generic handler for the tny_folder_get_msg_async
735  * call. It expects as user_data a #GetMsgAsyncHelper. This helper
736  * contains a user provided function that is called inside this
737  * method. This will allow us to use this callback in many different
738  * places. This callback performs the common actions for the
739  * get_msg_async call, more specific actions will be done by the user
740  * function
741  */
742 static void
743 get_msg_cb (TnyFolder *folder, TnyMsg *msg, GError **err, gpointer user_data)
744 {
745         GetMsgAsyncHelper *helper;
746
747         helper = (GetMsgAsyncHelper *) user_data;
748
749         if (*err && ((*err)->code == TNY_FOLDER_ERROR_GET_MSG)) {
750                 ModestHeaderView *header_view;
751                 ModestWidgetFactory *widget_factory;
752
753                 widget_factory = modest_main_window_get_widget_factory (helper->main_window);
754                 header_view = modest_widget_factory_get_header_view (widget_factory);
755                 g_object_unref (G_OBJECT (widget_factory));
756                 _modest_ui_actions_on_item_not_found (header_view, 
757                                                       MODEST_ITEM_TYPE_MESSAGE, 
758                                                       helper->main_window);
759                 return;
760         }
761
762         if (!msg)
763                 return;
764
765         /* Call user function */
766         helper->func (msg, user_data);
767
768         /* Process next element (if exists) */
769         tny_iterator_next (helper->iter);
770         if (tny_iterator_is_done (helper->iter)) {
771                 TnyList *headers;
772                 headers = tny_iterator_get_list (helper->iter);
773                 /* Free resources */
774                 g_object_unref (G_OBJECT (headers));
775                 g_object_unref (G_OBJECT (helper->iter));
776                 g_slice_free (GetMsgAsyncHelper, helper);
777         } else
778                 tny_folder_get_msg_async (folder, 
779                                           TNY_HEADER (tny_iterator_get_current (helper->iter)), 
780                                           get_msg_cb, helper);
781 }
782
783 void 
784 _modest_ui_actions_on_header_selected (ModestHeaderView *folder_view, 
785                                       TnyHeader *header,
786                                       ModestMainWindow *main_window)
787 {
788         TnyFolder *folder;
789         GetMsgAsyncHelper *helper;
790         TnyList *list;
791         TnyIterator *iter;
792
793         if (!header)
794                 return;
795
796         folder = tny_header_get_folder (TNY_HEADER(header));
797
798         /* Create list */
799         list = tny_simple_list_new ();
800         tny_list_prepend (list, G_OBJECT (header));
801
802         /* Fill helper data */
803         helper = g_slice_new0 (GetMsgAsyncHelper);
804         helper->main_window = main_window;
805         helper->iter = tny_list_create_iterator (list);
806         helper->func = read_msg_func;
807
808         tny_folder_get_msg_async (TNY_FOLDER(folder),
809                                   header,
810                                   get_msg_cb,
811                                   helper);
812
813         /* Frees */
814         g_object_unref (G_OBJECT (folder));
815 }
816
817 void 
818 _modest_ui_actions_on_folder_selection_changed (ModestFolderView *folder_view,
819                                                TnyFolder *folder, 
820                                                gboolean selected,
821                                                ModestMainWindow *main_window)
822 {
823         GtkLabel *folder_info_label;
824         TnyPlatformFactory *factory;
825         gchar *txt;     
826         ModestConf *conf;
827         ModestHeaderView *header_view;
828         ModestWidgetFactory *widget_factory;
829
830         widget_factory = modest_main_window_get_widget_factory (main_window);
831         folder_info_label = 
832                 GTK_LABEL (modest_widget_factory_get_folder_info_label (widget_factory));
833
834         if (!folder) {
835                 gtk_label_set_label (GTK_LABEL(folder_info_label), "");
836                 return;
837         }
838
839         factory = modest_tny_platform_factory_get_instance ();
840         header_view = modest_widget_factory_get_header_view (widget_factory);
841         conf = modest_tny_platform_factory_get_modest_conf_instance (factory);
842         g_object_unref (G_OBJECT (widget_factory));
843
844         if (!selected) { /* the folder was unselected; save it's settings  */
845                 modest_widget_memory_save (conf, G_OBJECT (header_view),
846                                            "header-view");
847         } else {  /* the folder was selected */
848                 guint num, unread;
849                 num    = tny_folder_get_all_count    (folder);
850                 unread = tny_folder_get_unread_count (folder);
851                         
852                 txt = g_strdup_printf (_("%d %s, %d unread"),
853                                        num, num==1 ? _("item") : _("items"), unread);           
854                 gtk_label_set_label (GTK_LABEL(folder_info_label), txt);
855                 g_free (txt);
856                         
857                 modest_header_view_set_folder (header_view, folder);
858                 modest_widget_memory_restore (conf, G_OBJECT(header_view),
859                                               "header-view");
860         }
861 }
862
863 void
864 _modest_ui_actions_on_password_requested (ModestTnyAccountStore *account_store, 
865                                          const gchar* account_name,
866                                          gchar **password, 
867                                          gboolean *cancel, 
868                                          gboolean *remember, 
869                                          ModestMainWindow *main_window)
870 {
871         gchar *txt;
872         GtkWidget *dialog, *entry, *remember_pass_check;
873
874         dialog = gtk_dialog_new_with_buttons (_("Password requested"),
875                                               GTK_WINDOW (main_window),
876                                               GTK_DIALOG_MODAL,
877                                               GTK_STOCK_CANCEL,
878                                               GTK_RESPONSE_REJECT,
879                                               GTK_STOCK_OK,
880                                               GTK_RESPONSE_ACCEPT,
881                                               NULL);
882
883         txt = g_strdup_printf (_("Please enter your password for %s"), account_name);
884         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), gtk_label_new(txt),
885                             FALSE, FALSE, 0);
886         g_free (txt);
887
888         entry = gtk_entry_new_with_max_length (40);
889         gtk_entry_set_visibility (GTK_ENTRY(entry), FALSE);
890         gtk_entry_set_invisible_char (GTK_ENTRY(entry), 0x2022); /* bullet unichar */
891         
892         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry,
893                             TRUE, FALSE, 0);    
894
895         remember_pass_check = gtk_check_button_new_with_label (_("Remember password"));
896         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), remember_pass_check,
897                             TRUE, FALSE, 0);
898
899         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
900         
901         if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
902                 *password = g_strdup (gtk_entry_get_text (GTK_ENTRY(entry)));
903                 *cancel   = FALSE;
904         } else {
905                 *password = NULL;
906                 *cancel   = TRUE;
907         }
908
909         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (remember_pass_check)))
910                 *remember = TRUE;
911         else
912                 *remember = FALSE;
913
914         gtk_widget_destroy (dialog);
915
916         while (gtk_events_pending ())
917                 gtk_main_iteration ();
918 }
919
920 /****************************************************/
921 /*
922  * below some stuff to clearup statusbar messages after 1,5 seconds....
923  */
924 typedef struct {
925         GtkWidget *status_bar;
926         GtkWidget *progress_bar;
927         guint     msg_id;
928 } StatusRemoveData;
929
930 static gboolean
931 on_statusbar_remove_msg (StatusRemoveData *data)
932 {
933         /* we need to test types, as this callback maybe called after the
934          * widgets have been destroyed
935          */
936         if (GTK_IS_STATUSBAR(data->status_bar)) 
937                 gtk_statusbar_remove (GTK_STATUSBAR(data->status_bar),
938                                       0, data->msg_id);
939         if (GTK_IS_PROGRESS_BAR(data->progress_bar))
940                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(data->progress_bar),
941                                                1.0);
942         g_free (data);
943         return FALSE;
944 }
945
946 static void
947 statusbar_push (ModestWidgetFactory *factory, guint context_id, const gchar *msg)
948 {
949         guint id;
950         StatusRemoveData *data;
951         GtkWidget *status_bar, *progress_bar;
952         
953         if (!msg)
954                 return;
955
956         progress_bar = modest_widget_factory_get_progress_bar (factory);
957         status_bar   = modest_widget_factory_get_status_bar (factory);
958         
959         id = gtk_statusbar_push (GTK_STATUSBAR(status_bar), 0, msg);
960
961         data = g_new (StatusRemoveData, 1);
962         data->status_bar   = status_bar;
963         data->progress_bar = progress_bar;
964         data->msg_id     = id;
965
966         g_timeout_add (1500, (GSourceFunc)on_statusbar_remove_msg, data);
967 }
968 /****************************************************************************/
969
970 void
971 _modest_ui_actions_on_connection_changed (TnyDevice *device, 
972                                          gboolean online,
973                                          ModestMainWindow *main_window)
974 {
975         GtkWidget *online_toggle;
976         ModestHeaderView *header_view;
977         ModestWidgetFactory *widget_factory;
978         
979         widget_factory = modest_main_window_get_widget_factory (main_window);
980         header_view   = modest_widget_factory_get_header_view (widget_factory);
981         online_toggle = modest_widget_factory_get_online_toggle (widget_factory);
982
983         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(online_toggle),
984                                       online);
985         gtk_button_set_label (GTK_BUTTON(online_toggle),
986                               online ? _("Online") : _("Offline"));
987
988         statusbar_push (widget_factory, 0, 
989                         online ? _("Modest went online") : _("Modest went offline"));
990         g_object_unref (G_OBJECT (widget_factory));
991
992         /* If Modest has became online and the header view has a
993            header selected then show it */
994         if (online) {
995                 GtkTreeSelection *selected;
996
997                 selected = gtk_tree_view_get_selection (GTK_TREE_VIEW (header_view));
998                 _modest_header_view_change_selection (selected, header_view);
999         }
1000 }
1001
1002 void
1003 _modest_ui_actions_on_online_toggle_toggled (GtkToggleButton *toggle,
1004                                              ModestMainWindow *main_window)
1005 {
1006         gboolean online;
1007         TnyDevice *device;
1008         TnyPlatformFactory *factory;
1009         TnyAccountStore *account_store;
1010
1011         /* Get device. Do not ask the platform factory for it, because
1012            it returns always a new one */
1013         factory = modest_tny_platform_factory_get_instance ();
1014         account_store = tny_platform_factory_new_account_store (factory);
1015         device = tny_account_store_get_device (account_store);
1016
1017         online  = gtk_toggle_button_get_active (toggle);
1018
1019         if (online)
1020                 tny_device_force_online (device);
1021         else
1022                 tny_device_force_offline (device);
1023 }
1024
1025 void 
1026 _modest_ui_actions_on_item_not_found (ModestHeaderView *header_view,
1027                                      ModestItemType type,
1028                                      ModestMainWindow *main_window)
1029 {
1030         GtkWidget *dialog;
1031         gchar *txt, *item;
1032         gboolean online;
1033         TnyDevice *device;
1034         TnyPlatformFactory *factory;
1035         TnyAccountStore *account_store;
1036
1037         item = (type == MODEST_ITEM_TYPE_FOLDER) ? "folder" : "message";
1038
1039         /* Get device. Do not ask the platform factory for it, because
1040            it returns always a new one */
1041         factory = modest_tny_platform_factory_get_instance ();
1042         account_store = tny_platform_factory_new_account_store (factory);
1043         device = tny_account_store_get_device (account_store);
1044         
1045         gdk_threads_enter ();
1046         online = tny_device_is_online (device);
1047
1048         if (online) {
1049                 /* already online -- the item is simply not there... */
1050                 dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
1051                                                  GTK_DIALOG_MODAL,
1052                                                  GTK_MESSAGE_WARNING,
1053                                                  GTK_BUTTONS_OK,
1054                                                  _("The %s you selected cannot be found"),
1055                                                  item);
1056                 gtk_dialog_run (GTK_DIALOG(dialog));
1057         } else {
1058
1059                 dialog = gtk_dialog_new_with_buttons (_("Connection requested"),
1060                                                       GTK_WINDOW (main_window),
1061                                                       GTK_DIALOG_MODAL,
1062                                                       GTK_STOCK_CANCEL,
1063                                                       GTK_RESPONSE_REJECT,
1064                                                       GTK_STOCK_OK,
1065                                                       GTK_RESPONSE_ACCEPT,
1066                                                       NULL);
1067
1068                 txt = g_strdup_printf (_("This %s is not available in offline mode.\n"
1069                                          "Do you want to get online?"), item);
1070                 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
1071                                     gtk_label_new (txt), FALSE, FALSE, 0);
1072                 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
1073                 g_free (txt);
1074
1075                 gtk_window_set_default_size (GTK_WINDOW(dialog), 300, 300);
1076                 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1077                         tny_device_force_online (device);
1078                 }
1079         }
1080         gtk_widget_destroy (dialog);
1081         gdk_threads_leave ();
1082 }
1083
1084 void
1085 _modest_ui_actions_on_header_status_update (ModestHeaderView *header_view, 
1086                                             const gchar *msg,
1087                                             gint num, 
1088                                             gint total, 
1089                                             ModestMainWindow *main_window)
1090 {
1091         GtkWidget *progress_bar;
1092         ModestWidgetFactory *widget_factory;
1093
1094         widget_factory = modest_main_window_get_widget_factory (main_window);
1095         progress_bar = modest_widget_factory_get_progress_bar (widget_factory);
1096
1097         if (total != 0)
1098                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar),
1099                                                (gdouble)num/(gdouble)total);
1100         else
1101                 gtk_progress_bar_pulse (GTK_PROGRESS_BAR(progress_bar));
1102
1103         statusbar_push (widget_factory, 0, msg);
1104
1105         /* Free */
1106         g_object_unref (G_OBJECT (widget_factory));
1107 }
1108
1109
1110 void
1111 _modest_ui_actions_on_msg_link_hover (ModestMsgView *msgview, 
1112                                       const gchar* link,
1113                                       ModestMainWindow *main_window)
1114 {
1115         ModestWidgetFactory *widget_factory;
1116
1117         widget_factory = modest_main_window_get_widget_factory (main_window);
1118         statusbar_push (widget_factory, 0, link);
1119         g_object_unref (G_OBJECT (widget_factory));     
1120
1121         /* TODO: do something */
1122 }       
1123
1124
1125 void
1126 _modest_ui_actions_on_msg_link_clicked (ModestMsgView *msgview, 
1127                                         const gchar* link,
1128                                         ModestMainWindow *main_window)
1129 {
1130         gchar *msg;
1131         ModestWidgetFactory *widget_factory;
1132
1133         msg = g_strdup_printf (_("Opening %s..."), link);
1134         widget_factory = modest_main_window_get_widget_factory (main_window);
1135         statusbar_push (widget_factory, 0, msg);
1136
1137         g_object_unref (G_OBJECT (widget_factory));     
1138
1139         g_free (msg);
1140
1141         /* TODO: do something */
1142 }
1143
1144 void
1145 _modest_ui_actions_on_msg_attachment_clicked (ModestMsgView *msgview, 
1146                                               int index,
1147                                               ModestMainWindow *main_window)
1148 {
1149         gchar *msg;
1150         ModestWidgetFactory *widget_factory;
1151         
1152         msg = g_strdup_printf (_("Opening attachment %d..."), index);
1153         widget_factory = modest_main_window_get_widget_factory (main_window);
1154         statusbar_push (widget_factory, 0, msg);
1155         
1156         g_free (msg);
1157         g_object_unref (G_OBJECT (widget_factory));
1158         /* TODO: do something */
1159 }
1160
1161 void
1162 _modest_ui_actions_on_send (GtkWidget *widget, 
1163                             ModestEditMsgWindow *edit_window)
1164 {
1165         TnyTransportAccount *transport_account;
1166         ModestMailOperation *mail_operation;
1167         MsgData *data;
1168
1169         data = modest_edit_msg_window_get_msg_data (edit_window);
1170
1171         /* FIXME: Code added just for testing. The final version will
1172            use the send queue provided by tinymail and some
1173            classifier */
1174         {
1175                 TnyList *accounts;
1176                 TnyIterator *iter;
1177                 TnyAccountStore *account_store;
1178
1179                 accounts = TNY_LIST(tny_simple_list_new ());
1180                 account_store = modest_edit_msg_window_get_account_store (edit_window);
1181                 tny_account_store_get_accounts (account_store, accounts,
1182                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
1183                 g_object_unref (G_OBJECT (account_store));
1184
1185                 iter = tny_list_create_iterator(accounts);
1186                 tny_iterator_first (iter);
1187                 if (tny_iterator_is_done (iter)) {
1188                         /* FIXME: Add error handling through mail operation */
1189                         g_printerr("modest: no transport accounts defined\n");
1190                         modest_edit_msg_window_free_msg_data (edit_window, data);
1191                         return;
1192                 }
1193                 transport_account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current(iter));
1194                 g_object_ref (transport_account);
1195
1196                 tny_list_foreach (accounts, (GFunc) g_object_unref, NULL);
1197                 g_object_unref (G_OBJECT (accounts));
1198                 g_object_unref (G_OBJECT (iter));
1199         }
1200
1201         mail_operation = modest_mail_operation_new ();
1202
1203         modest_mail_operation_send_new_mail (mail_operation,
1204                                              transport_account,
1205                                              data->from, 
1206                                              data->to, 
1207                                              data->cc, 
1208                                              data->bcc,
1209                                              data->subject, 
1210                                              data->body, 
1211                                              NULL);
1212         /* Frees */
1213         g_object_unref (G_OBJECT (mail_operation));
1214         g_object_unref (G_OBJECT (transport_account));
1215         modest_edit_msg_window_free_msg_data (edit_window, data);
1216
1217         /* Save settings and close the window */
1218         /* save_settings (edit_window) */
1219         gtk_widget_destroy (GTK_WIDGET (edit_window));
1220 }