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