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