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