- modest-ui.c:
[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                 gtk_window_set_title (GTK_WINDOW(main_window), "Modest");
849         } else {  /* the folder was selected */
850                 guint num, unread;
851                 gchar *title;
852                 num    = tny_folder_get_all_count    (folder);
853                 unread = tny_folder_get_unread_count (folder);
854
855                 title = g_strdup_printf ("Modest: %s",
856                                          tny_folder_get_name (folder));
857                 
858                 gtk_window_set_title (GTK_WINDOW(main_window), title);
859                 g_free (title);
860
861                 txt = g_strdup_printf (_("%d %s, %d unread"),
862                                        num, num==1 ? _("item") : _("items"), unread);           
863                 gtk_label_set_label (GTK_LABEL(folder_info_label), txt);
864                 g_free (txt);
865                         
866                 modest_header_view_set_folder (header_view, folder);
867                 modest_widget_memory_restore (conf, G_OBJECT(header_view),
868                                               "header-view");
869         }
870 }
871
872 void
873 _modest_ui_actions_on_password_requested (ModestTnyAccountStore *account_store, 
874                                          const gchar* account_name,
875                                          gchar **password, 
876                                          gboolean *cancel, 
877                                          gboolean *remember, 
878                                          ModestMainWindow *main_window)
879 {
880         gchar *txt;
881         GtkWidget *dialog, *entry, *remember_pass_check;
882
883         dialog = gtk_dialog_new_with_buttons (_("Password requested"),
884                                               GTK_WINDOW (main_window),
885                                               GTK_DIALOG_MODAL,
886                                               GTK_STOCK_CANCEL,
887                                               GTK_RESPONSE_REJECT,
888                                               GTK_STOCK_OK,
889                                               GTK_RESPONSE_ACCEPT,
890                                               NULL);
891
892         txt = g_strdup_printf (_("Please enter your password for %s"), account_name);
893         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), gtk_label_new(txt),
894                             FALSE, FALSE, 0);
895         g_free (txt);
896
897         entry = gtk_entry_new_with_max_length (40);
898         gtk_entry_set_visibility (GTK_ENTRY(entry), FALSE);
899         gtk_entry_set_invisible_char (GTK_ENTRY(entry), 0x2022); /* bullet unichar */
900         
901         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry,
902                             TRUE, FALSE, 0);    
903
904         remember_pass_check = gtk_check_button_new_with_label (_("Remember password"));
905         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), remember_pass_check,
906                             TRUE, FALSE, 0);
907
908         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
909         
910         if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
911                 *password = g_strdup (gtk_entry_get_text (GTK_ENTRY(entry)));
912                 *cancel   = FALSE;
913         } else {
914                 *password = NULL;
915                 *cancel   = TRUE;
916         }
917
918         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (remember_pass_check)))
919                 *remember = TRUE;
920         else
921                 *remember = FALSE;
922
923         gtk_widget_destroy (dialog);
924
925         while (gtk_events_pending ())
926                 gtk_main_iteration ();
927 }
928
929 /****************************************************/
930 /*
931  * below some stuff to clearup statusbar messages after 1,5 seconds....
932  */
933 typedef struct {
934         GtkWidget *status_bar;
935         GtkWidget *progress_bar;
936         guint     msg_id;
937 } StatusRemoveData;
938
939 static gboolean
940 on_statusbar_remove_msg (StatusRemoveData *data)
941 {
942         /* we need to test types, as this callback maybe called after the
943          * widgets have been destroyed
944          */
945         if (GTK_IS_STATUSBAR(data->status_bar)) 
946                 gtk_statusbar_remove (GTK_STATUSBAR(data->status_bar),
947                                       0, data->msg_id);
948         if (GTK_IS_PROGRESS_BAR(data->progress_bar))
949                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(data->progress_bar),
950                                                1.0);
951         g_free (data);
952         return FALSE;
953 }
954
955 static void
956 statusbar_push (ModestWidgetFactory *factory, guint context_id, const gchar *msg)
957 {
958         guint id;
959         StatusRemoveData *data;
960         GtkWidget *status_bar, *progress_bar;
961         
962         if (!msg)
963                 return;
964
965         progress_bar = modest_widget_factory_get_progress_bar (factory);
966         status_bar   = modest_widget_factory_get_status_bar (factory);
967         
968         id = gtk_statusbar_push (GTK_STATUSBAR(status_bar), 0, msg);
969
970         data = g_new (StatusRemoveData, 1);
971         data->status_bar   = status_bar;
972         data->progress_bar = progress_bar;
973         data->msg_id     = id;
974
975         g_timeout_add (1500, (GSourceFunc)on_statusbar_remove_msg, data);
976 }
977 /****************************************************************************/
978
979 void
980 _modest_ui_actions_on_connection_changed (TnyDevice *device, 
981                                          gboolean online,
982                                          ModestMainWindow *main_window)
983 {
984         GtkWidget *online_toggle;
985         ModestHeaderView *header_view;
986         ModestWidgetFactory *widget_factory;
987         
988         widget_factory = modest_main_window_get_widget_factory (main_window);
989         header_view   = modest_widget_factory_get_header_view (widget_factory);
990         online_toggle = modest_widget_factory_get_online_toggle (widget_factory);
991
992         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(online_toggle),
993                                       online);
994         gtk_button_set_label (GTK_BUTTON(online_toggle),
995                               online ? _("Online") : _("Offline"));
996
997         statusbar_push (widget_factory, 0, 
998                         online ? _("Modest went online") : _("Modest went offline"));
999         g_object_unref (G_OBJECT (widget_factory));
1000
1001         /* If Modest has became online and the header view has a
1002            header selected then show it */
1003         if (online) {
1004                 GtkTreeSelection *selected;
1005
1006                 selected = gtk_tree_view_get_selection (GTK_TREE_VIEW (header_view));
1007                 _modest_header_view_change_selection (selected, header_view);
1008         }
1009 }
1010
1011 void
1012 _modest_ui_actions_on_online_toggle_toggled (GtkToggleButton *toggle,
1013                                              ModestMainWindow *main_window)
1014 {
1015         gboolean online;
1016         TnyDevice *device;
1017         TnyPlatformFactory *factory;
1018         TnyAccountStore *account_store;
1019
1020         /* Get device. Do not ask the platform factory for it, because
1021            it returns always a new one */
1022         factory = modest_tny_platform_factory_get_instance ();
1023         account_store = tny_platform_factory_new_account_store (factory);
1024         device = tny_account_store_get_device (account_store);
1025
1026         online  = gtk_toggle_button_get_active (toggle);
1027
1028         if (online)
1029                 tny_device_force_online (device);
1030         else
1031                 tny_device_force_offline (device);
1032 }
1033
1034 void 
1035 _modest_ui_actions_on_item_not_found (ModestHeaderView *header_view,
1036                                      ModestItemType type,
1037                                      ModestMainWindow *main_window)
1038 {
1039         GtkWidget *dialog;
1040         gchar *txt, *item;
1041         gboolean online;
1042         TnyDevice *device;
1043         TnyPlatformFactory *factory;
1044         TnyAccountStore *account_store;
1045
1046         item = (type == MODEST_ITEM_TYPE_FOLDER) ? "folder" : "message";
1047
1048         /* Get device. Do not ask the platform factory for it, because
1049            it returns always a new one */
1050         factory = modest_tny_platform_factory_get_instance ();
1051         account_store = tny_platform_factory_new_account_store (factory);
1052         device = tny_account_store_get_device (account_store);
1053         
1054         gdk_threads_enter ();
1055         online = tny_device_is_online (device);
1056
1057         if (online) {
1058                 /* already online -- the item is simply not there... */
1059                 dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
1060                                                  GTK_DIALOG_MODAL,
1061                                                  GTK_MESSAGE_WARNING,
1062                                                  GTK_BUTTONS_OK,
1063                                                  _("The %s you selected cannot be found"),
1064                                                  item);
1065                 gtk_dialog_run (GTK_DIALOG(dialog));
1066         } else {
1067
1068                 dialog = gtk_dialog_new_with_buttons (_("Connection requested"),
1069                                                       GTK_WINDOW (main_window),
1070                                                       GTK_DIALOG_MODAL,
1071                                                       GTK_STOCK_CANCEL,
1072                                                       GTK_RESPONSE_REJECT,
1073                                                       GTK_STOCK_OK,
1074                                                       GTK_RESPONSE_ACCEPT,
1075                                                       NULL);
1076
1077                 txt = g_strdup_printf (_("This %s is not available in offline mode.\n"
1078                                          "Do you want to get online?"), item);
1079                 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
1080                                     gtk_label_new (txt), FALSE, FALSE, 0);
1081                 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
1082                 g_free (txt);
1083
1084                 gtk_window_set_default_size (GTK_WINDOW(dialog), 300, 300);
1085                 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1086                         tny_device_force_online (device);
1087                 }
1088         }
1089         gtk_widget_destroy (dialog);
1090         gdk_threads_leave ();
1091 }
1092
1093 void
1094 _modest_ui_actions_on_header_status_update (ModestHeaderView *header_view, 
1095                                             const gchar *msg,
1096                                             gint num, 
1097                                             gint total, 
1098                                             ModestMainWindow *main_window)
1099 {
1100         GtkWidget *progress_bar;
1101         ModestWidgetFactory *widget_factory;
1102
1103         widget_factory = modest_main_window_get_widget_factory (main_window);
1104         progress_bar = modest_widget_factory_get_progress_bar (widget_factory);
1105
1106         if (total != 0)
1107                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar),
1108                                                (gdouble)num/(gdouble)total);
1109         else
1110                 gtk_progress_bar_pulse (GTK_PROGRESS_BAR(progress_bar));
1111
1112         statusbar_push (widget_factory, 0, msg);
1113
1114         /* Free */
1115         g_object_unref (G_OBJECT (widget_factory));
1116 }
1117
1118
1119 void
1120 _modest_ui_actions_on_msg_link_hover (ModestMsgView *msgview, 
1121                                       const gchar* link,
1122                                       ModestMainWindow *main_window)
1123 {
1124         ModestWidgetFactory *widget_factory;
1125
1126         widget_factory = modest_main_window_get_widget_factory (main_window);
1127         statusbar_push (widget_factory, 0, link);
1128         g_object_unref (G_OBJECT (widget_factory));     
1129
1130         /* TODO: do something */
1131 }       
1132
1133
1134 void
1135 _modest_ui_actions_on_msg_link_clicked (ModestMsgView *msgview, 
1136                                         const gchar* link,
1137                                         ModestMainWindow *main_window)
1138 {
1139         gchar *msg;
1140         ModestWidgetFactory *widget_factory;
1141
1142         msg = g_strdup_printf (_("Opening %s..."), link);
1143         widget_factory = modest_main_window_get_widget_factory (main_window);
1144         statusbar_push (widget_factory, 0, msg);
1145
1146         g_object_unref (G_OBJECT (widget_factory));     
1147
1148         g_free (msg);
1149
1150         /* TODO: do something */
1151 }
1152
1153 void
1154 _modest_ui_actions_on_msg_attachment_clicked (ModestMsgView *msgview, 
1155                                               int index,
1156                                               ModestMainWindow *main_window)
1157 {
1158         gchar *msg;
1159         ModestWidgetFactory *widget_factory;
1160         
1161         msg = g_strdup_printf (_("Opening attachment %d..."), index);
1162         widget_factory = modest_main_window_get_widget_factory (main_window);
1163         statusbar_push (widget_factory, 0, msg);
1164         
1165         g_free (msg);
1166         g_object_unref (G_OBJECT (widget_factory));
1167         /* TODO: do something */
1168 }
1169
1170 void
1171 _modest_ui_actions_on_send (GtkWidget *widget, 
1172                             ModestEditMsgWindow *edit_window)
1173 {
1174         TnyTransportAccount *transport_account;
1175         ModestMailOperation *mail_operation;
1176         MsgData *data;
1177
1178         data = modest_edit_msg_window_get_msg_data (edit_window);
1179
1180         /* FIXME: Code added just for testing. The final version will
1181            use the send queue provided by tinymail and some
1182            classifier */
1183         {
1184                 TnyList *accounts;
1185                 TnyIterator *iter;
1186                 TnyAccountStore *account_store;
1187
1188                 accounts = TNY_LIST(tny_simple_list_new ());
1189                 account_store = modest_edit_msg_window_get_account_store (edit_window);
1190                 tny_account_store_get_accounts (account_store, accounts,
1191                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
1192                 g_object_unref (G_OBJECT (account_store));
1193
1194                 iter = tny_list_create_iterator(accounts);
1195                 tny_iterator_first (iter);
1196                 if (tny_iterator_is_done (iter)) {
1197                         /* FIXME: Add error handling through mail operation */
1198                         g_printerr("modest: no transport accounts defined\n");
1199                         modest_edit_msg_window_free_msg_data (edit_window, data);
1200                         return;
1201                 }
1202                 transport_account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current(iter));
1203                 g_object_ref (transport_account);
1204
1205                 tny_list_foreach (accounts, (GFunc) g_object_unref, NULL);
1206                 g_object_unref (G_OBJECT (accounts));
1207                 g_object_unref (G_OBJECT (iter));
1208         }
1209
1210         mail_operation = modest_mail_operation_new ();
1211
1212         modest_mail_operation_send_new_mail (mail_operation,
1213                                              transport_account,
1214                                              data->from, 
1215                                              data->to, 
1216                                              data->cc, 
1217                                              data->bcc,
1218                                              data->subject, 
1219                                              data->body, 
1220                                              NULL);
1221         /* Frees */
1222         g_object_unref (G_OBJECT (mail_operation));
1223         g_object_unref (G_OBJECT (transport_account));
1224         modest_edit_msg_window_free_msg_data (edit_window, data);
1225
1226         /* Save settings and close the window */
1227         /* save_settings (edit_window) */
1228         gtk_widget_destroy (GTK_WIDGET (edit_window));
1229 }