Show connection requested dialog when the message is not found
[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)) || !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         /* Call user function */
725         helper->func (msg, user_data);
726
727         /* Process next element (if exists) */
728         tny_iterator_next (helper->iter);
729         if (tny_iterator_is_done (helper->iter)) {
730                 TnyList *headers;
731                 headers = tny_iterator_get_list (helper->iter);
732                 /* Free resources */
733                 g_object_unref (G_OBJECT (headers));
734                 g_object_unref (G_OBJECT (helper->iter));
735                 g_slice_free (GetMsgAsyncHelper, helper);
736         } else
737                 tny_folder_get_msg_async (folder, 
738                                           TNY_HEADER (tny_iterator_get_current (helper->iter)), 
739                                           get_msg_cb, helper);
740 }
741
742 void 
743 _modest_ui_actions_on_header_selected (ModestHeaderView *folder_view, 
744                                       TnyHeader *header,
745                                       ModestMainWindow *main_window)
746 {
747         TnyFolder *folder;
748         GetMsgAsyncHelper *helper;
749         TnyList *list;
750
751         /* when there's no header, clear the msgview */
752         if (!header) {
753                 ModestMsgView *msg_view;
754                 msg_view       = modest_widget_factory_get_msg_preview
755                         (modest_runtime_get_widget_factory());
756                 modest_msg_view_set_message (msg_view, NULL);
757                 return;
758         }
759
760         folder = tny_header_get_folder (TNY_HEADER(header));
761
762         /* Create list */
763         list = tny_simple_list_new ();
764         tny_list_prepend (list, G_OBJECT (header));
765
766         /* Fill helper data */
767         helper = g_slice_new0 (GetMsgAsyncHelper);
768         helper->main_window = main_window;
769         helper->iter = tny_list_create_iterator (list);
770         helper->func = read_msg_func;
771
772         tny_folder_get_msg_async (TNY_FOLDER(folder),
773                                   header, get_msg_cb,
774                                   helper);
775
776         /* Frees */
777         g_object_unref (G_OBJECT (folder));
778 }
779
780 void 
781 _modest_ui_actions_on_folder_selection_changed (ModestFolderView *folder_view,
782                                                TnyFolder *folder, 
783                                                gboolean selected,
784                                                ModestMainWindow *main_window)
785 {
786         GtkLabel *folder_info_label;
787         gchar *txt;     
788         ModestConf *conf;
789         ModestHeaderView *header_view;
790
791         folder_info_label = 
792                 GTK_LABEL (modest_widget_factory_get_folder_info_label
793                            (modest_runtime_get_widget_factory()));
794
795         if (!folder) {
796                 gtk_label_set_label (GTK_LABEL(folder_info_label), "");
797                 return;
798         }
799         
800         header_view = modest_widget_factory_get_header_view (modest_runtime_get_widget_factory());
801         conf = modest_runtime_get_conf ();
802
803         if (!selected) { /* the folder was unselected; save it's settings  */
804                 modest_widget_memory_save (conf, G_OBJECT (header_view),
805                                            "header-view");
806                 gtk_window_set_title (GTK_WINDOW(main_window), "Modest");
807                 modest_header_view_set_folder (header_view, NULL);
808         } else {  /* the folder was selected */
809                 if (folder) { /* folder may be NULL */
810                         guint num, unread;
811                         gchar *title;
812
813                         num    = tny_folder_get_all_count    (folder);
814                         unread = tny_folder_get_unread_count (folder);
815                         
816                         title = g_strdup_printf ("Modest: %s",
817                                                  tny_folder_get_name (folder));
818                         
819                         gtk_window_set_title (GTK_WINDOW(main_window), title);
820                         g_free (title);
821                         
822                         txt = g_strdup_printf (_("%d %s, %d unread"),
823                                        num, num==1 ? _("item") : _("items"), unread);           
824                         gtk_label_set_label (GTK_LABEL(folder_info_label), txt);
825                         g_free (txt);
826                 }
827                 modest_header_view_set_folder (header_view, folder);
828                 modest_widget_memory_restore (conf, G_OBJECT(header_view),
829                                               "header-view");
830         }
831 }
832
833
834 /****************************************************/
835 /*
836  * below some stuff to clearup statusbar messages after 1,5 seconds....
837  */
838 typedef struct {
839         GtkWidget *status_bar;
840         GtkWidget *progress_bar;
841         guint     msg_id;
842 } StatusRemoveData;
843
844
845 static gboolean
846 progress_bar_clean (GtkWidget *bar)
847 {
848         if (GTK_IS_PROGRESS_BAR(bar)) {
849                 gtk_progress_bar_set_text     (GTK_PROGRESS_BAR(bar), "");
850                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(bar), 1.0);
851         }
852         return FALSE;
853 }
854
855 static gboolean
856 statusbar_clean (GtkWidget *bar)
857 {
858         if (GTK_IS_STATUSBAR(bar))
859                 gtk_statusbar_push (GTK_STATUSBAR(bar), 0, "");
860         return FALSE;
861 }
862
863
864 static void
865 statusbar_push (ModestWidgetFactory *factory, guint context_id, const gchar *msg)
866 {
867         GtkWidget *status_bar, *progress_bar;
868         
869         if (!msg)
870                 return;
871
872         progress_bar = modest_widget_factory_get_progress_bar (factory);
873         status_bar   = modest_widget_factory_get_status_bar (factory);
874
875         gtk_widget_show (GTK_WIDGET(status_bar));
876         gtk_widget_show (GTK_WIDGET(progress_bar));
877
878         gtk_statusbar_push (GTK_STATUSBAR(status_bar), 0, msg);
879
880         g_timeout_add (1500, (GSourceFunc)statusbar_clean, status_bar);
881         g_timeout_add (3000, (GSourceFunc)progress_bar_clean, progress_bar);
882 }
883 /****************************************************************************/
884
885 void
886 _modest_ui_actions_on_connection_changed (TnyDevice *device, gboolean online,
887                                           ModestMainWindow *main_window)
888 {
889         GtkWidget *online_toggle;
890         ModestHeaderView *header_view;
891         ModestWidgetFactory *widget_factory;
892
893         g_return_if_fail (main_window);
894         
895         widget_factory = modest_runtime_get_widget_factory ();
896         header_view   = modest_widget_factory_get_header_view (widget_factory);
897         online_toggle = modest_widget_factory_get_online_toggle (widget_factory);
898
899         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(online_toggle),
900                                       online);
901         gtk_button_set_label (GTK_BUTTON(online_toggle),
902                               online ? _("Online") : _("Offline"));
903
904         statusbar_push (widget_factory, 0, 
905                         online ? _("Modest went online") : _("Modest went offline"));
906         
907         /* If Modest has became online and the header view has a
908            header selected then show it */
909         if (online) {
910                 GtkTreeSelection *selected;
911
912                 selected = gtk_tree_view_get_selection (GTK_TREE_VIEW (header_view));
913                 _modest_header_view_change_selection (selected, header_view);
914         }
915 }
916
917 void
918 _modest_ui_actions_on_online_toggle_toggled (GtkToggleButton *toggle,
919                                              ModestMainWindow *main_window)
920 {
921         gboolean online;
922         TnyDevice *device;
923
924         device = tny_account_store_get_device
925                 (TNY_ACCOUNT_STORE(modest_runtime_get_account_store()));
926
927         online  = gtk_toggle_button_get_active (toggle);
928
929         if (online)
930                 tny_device_force_online (device);
931         else
932                 tny_device_force_offline (device);
933 }
934
935 void 
936 _modest_ui_actions_on_item_not_found (ModestHeaderView *header_view,
937                                      ModestItemType type,
938                                      ModestMainWindow *main_window)
939 {
940         GtkWidget *dialog;
941         gchar *txt, *item;
942         gboolean online;
943         TnyDevice *device;
944         TnyPlatformFactory *factory;
945         TnyAccountStore *account_store;
946
947         item = (type == MODEST_ITEM_TYPE_FOLDER) ? "folder" : "message";
948
949         /* Get device. Do not ask the platform factory for it, because
950            it returns always a new one */
951         factory = modest_tny_platform_factory_get_instance ();
952         account_store = tny_platform_factory_new_account_store (factory);
953         device = tny_account_store_get_device (account_store);
954
955         if (g_main_depth > 0)   
956                 gdk_threads_enter ();
957         online = tny_device_is_online (device);
958
959         if (online) {
960                 /* already online -- the item is simply not there... */
961                 dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
962                                                  GTK_DIALOG_MODAL,
963                                                  GTK_MESSAGE_WARNING,
964                                                  GTK_BUTTONS_OK,
965                                                  _("The %s you selected cannot be found"),
966                                                  item);
967                 gtk_dialog_run (GTK_DIALOG(dialog));
968         } else {
969
970                 dialog = gtk_dialog_new_with_buttons (_("Connection requested"),
971                                                       GTK_WINDOW (main_window),
972                                                       GTK_DIALOG_MODAL,
973                                                       GTK_STOCK_CANCEL,
974                                                       GTK_RESPONSE_REJECT,
975                                                       GTK_STOCK_OK,
976                                                       GTK_RESPONSE_ACCEPT,
977                                                       NULL);
978
979                 txt = g_strdup_printf (_("This %s is not available in offline mode.\n"
980                                          "Do you want to get online?"), item);
981                 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
982                                     gtk_label_new (txt), FALSE, FALSE, 0);
983                 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
984                 g_free (txt);
985
986                 gtk_window_set_default_size (GTK_WINDOW(dialog), 300, 300);
987                 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
988                         tny_device_force_online (device);
989                 }
990         }
991         gtk_widget_destroy (dialog);
992         if (g_main_depth > 0)   
993                 gdk_threads_leave ();
994 }
995
996
997
998 void
999 _modest_ui_actions_on_header_status_update (ModestHeaderView *header_view, 
1000                                             const gchar *msg,
1001                                             gint num, 
1002                                             gint total, 
1003                                             ModestMainWindow *main_window)
1004 {
1005         GtkWidget *progress_bar;
1006         char* txt;
1007         
1008         progress_bar = modest_widget_factory_get_progress_bar
1009                 (modest_runtime_get_widget_factory());
1010         if (total != 0)
1011                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress_bar),
1012                                                (gdouble)num/(gdouble)total);
1013         else
1014                 gtk_progress_bar_pulse (GTK_PROGRESS_BAR(progress_bar));
1015
1016         txt = g_strdup_printf (_("Downloading %d of %d"), num, total);
1017         gtk_progress_bar_set_text (GTK_PROGRESS_BAR(progress_bar), txt);
1018         g_free (txt);
1019         
1020         statusbar_push (modest_runtime_get_widget_factory(), 0, msg);
1021 }
1022
1023
1024
1025 void
1026 _modest_ui_actions_on_msg_link_hover (ModestMsgView *msgview, 
1027                                       const gchar* link,
1028                                       ModestMainWindow *main_window)
1029 {
1030         statusbar_push (modest_runtime_get_widget_factory(), 0, link);
1031
1032         /* TODO: do something */
1033 }       
1034
1035
1036 void
1037 _modest_ui_actions_on_msg_link_clicked (ModestMsgView *msgview, 
1038                                         const gchar* link,
1039                                         ModestMainWindow *main_window)
1040 {
1041         gchar *msg;
1042
1043         msg = g_strdup_printf (_("Opening %s..."), link);
1044         statusbar_push (modest_runtime_get_widget_factory(), 0, msg);
1045         g_free (msg);
1046
1047         /* TODO: do something */
1048 }
1049
1050 void
1051 _modest_ui_actions_on_msg_attachment_clicked (ModestMsgView *msgview, 
1052                                               int index,
1053                                               ModestMainWindow *main_window)
1054 {
1055         gchar *msg;
1056         
1057         msg = g_strdup_printf (_("Opening attachment %d..."), index);
1058         statusbar_push (modest_runtime_get_widget_factory(), 0, msg);
1059         
1060         g_free (msg);
1061         /* TODO: do something */
1062 }
1063
1064 void
1065 _modest_ui_actions_on_send (GtkWidget *widget, 
1066                             ModestEditMsgWindow *edit_window)
1067 {
1068         TnyTransportAccount *transport_account;
1069         ModestMailOperation *mail_operation;
1070         MsgData *data;
1071
1072         data = modest_edit_msg_window_get_msg_data (edit_window);
1073
1074         /* FIXME: Code added just for testing. The final version will
1075            use the send queue provided by tinymail and some
1076            classifier */
1077         {
1078                 TnyList *accounts;
1079                 TnyIterator *iter;
1080                 
1081                 accounts = TNY_LIST(tny_simple_list_new ());
1082                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(modest_runtime_get_account_store()),
1083                                                 accounts,
1084                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
1085
1086                 iter = tny_list_create_iterator(accounts);
1087                 tny_iterator_first (iter);
1088                 if (tny_iterator_is_done (iter)) {
1089                         /* FIXME: Add error handling through mail operation */
1090                         g_printerr("modest: no transport accounts defined\n");
1091                         modest_edit_msg_window_free_msg_data (edit_window, data);
1092                         return;
1093                 }
1094                 transport_account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current(iter));
1095                 g_object_ref (transport_account);
1096
1097                 tny_list_foreach (accounts, (GFunc) g_object_unref, NULL);
1098                 g_object_unref (G_OBJECT (accounts));
1099                 g_object_unref (G_OBJECT (iter));
1100         }
1101
1102         mail_operation = modest_mail_operation_new ();
1103
1104         modest_mail_operation_send_new_mail (mail_operation,
1105                                              transport_account,
1106                                              data->from, 
1107                                              data->to, 
1108                                              data->cc, 
1109                                              data->bcc,
1110                                              data->subject, 
1111                                              data->body, 
1112                                              NULL);
1113         /* Frees */
1114         g_object_unref (G_OBJECT (mail_operation));
1115         g_object_unref (G_OBJECT (transport_account));
1116         modest_edit_msg_window_free_msg_data (edit_window, data);
1117
1118         /* Save settings and close the window */
1119         /* save_settings (edit_window) */
1120         gtk_widget_destroy (GTK_WIDGET (edit_window));
1121 }
1122
1123 /*
1124  * Shows a dialog with an entry that asks for some text. The returned
1125  * value must be freed by the caller. The dialog window title will be
1126  * set to @title.
1127  */
1128 static gchar *
1129 ask_for_folder_name (GtkWindow *parent_window,
1130                      const gchar *title)
1131 {
1132         GtkWidget *dialog, *entry;
1133         gchar *folder_name = NULL;
1134
1135         /* Ask for folder name */
1136         dialog = gtk_dialog_new_with_buttons (_("New Folder Name"),
1137                                               parent_window,
1138                                               GTK_DIALOG_MODAL,
1139                                               GTK_STOCK_CANCEL,
1140                                               GTK_RESPONSE_REJECT,
1141                                               GTK_STOCK_OK,
1142                                               GTK_RESPONSE_ACCEPT,
1143                                               NULL);
1144         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
1145                             gtk_label_new(title),
1146                             FALSE, FALSE, 0);
1147                 
1148         entry = gtk_entry_new_with_max_length (40);
1149         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
1150                             entry,
1151                             TRUE, FALSE, 0);    
1152         
1153         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
1154         
1155         if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)         
1156                 folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
1157
1158         gtk_widget_destroy (dialog);
1159
1160         return folder_name;
1161 }
1162         
1163 void 
1164 _modest_ui_actions_on_new_folder (GtkWidget *widget,
1165                                   ModestMainWindow *main_window)
1166 {
1167         TnyFolder *parent_folder;
1168         ModestFolderView *folder_view;
1169
1170         folder_view = modest_widget_factory_get_folder_view
1171                 (modest_runtime_get_widget_factory());
1172         parent_folder = modest_folder_view_get_selected (folder_view);
1173         
1174         if (parent_folder) {
1175                 gchar *folder_name;
1176
1177                 folder_name = ask_for_folder_name (GTK_WINDOW (main_window),
1178                                                    _("Please enter a name for the new folder"));
1179
1180                 if (folder_name != NULL && strlen (folder_name) > 0) {
1181                         TnyFolder *new_folder;
1182                         ModestMailOperation *mail_op;
1183
1184                         mail_op = modest_mail_operation_new ();
1185                         new_folder = modest_mail_operation_create_folder (mail_op,
1186                                                                           TNY_FOLDER_STORE (parent_folder),
1187                                                                           (const gchar *) folder_name);
1188                         if (new_folder) {
1189                                 /* Do anything more? The model
1190                                    is automatically updated */
1191                                 g_object_unref (new_folder);
1192                         }
1193                         g_object_unref (mail_op);
1194                 }
1195                 g_object_unref (parent_folder);
1196         }
1197 }
1198
1199 void 
1200 _modest_ui_actions_on_rename_folder (GtkWidget *widget,
1201                                      ModestMainWindow *main_window)
1202 {
1203         TnyFolder *folder;
1204         ModestFolderView *folder_view;
1205         
1206         folder_view = modest_widget_factory_get_folder_view (modest_runtime_get_widget_factory());
1207         folder = modest_folder_view_get_selected (folder_view);
1208
1209         if (folder) {
1210                 gchar *folder_name;
1211
1212                 folder_name = ask_for_folder_name (GTK_WINDOW (main_window),
1213                                                    _("Please enter a new name for the folder"));
1214
1215                 if (folder_name != NULL && strlen (folder_name) > 0) {
1216                         ModestMailOperation *mail_op;
1217
1218                         mail_op = modest_mail_operation_new ();
1219                         modest_mail_operation_rename_folder (mail_op,
1220                                                              folder,
1221                                                              (const gchar *) folder_name);
1222                         g_object_unref (mail_op);
1223                 }
1224                 g_object_unref (folder);
1225         }
1226 }
1227
1228 static void
1229 delete_folder (ModestMainWindow *main_window,
1230                gboolean move_to_trash) 
1231 {
1232         TnyFolder *folder;
1233         ModestFolderView *folder_view;
1234         ModestMailOperation *mail_op;
1235         
1236         folder_view = modest_widget_factory_get_folder_view (modest_runtime_get_widget_factory());
1237         folder = modest_folder_view_get_selected (folder_view);
1238
1239         mail_op = modest_mail_operation_new ();
1240         modest_mail_operation_remove_folder (mail_op, folder, move_to_trash);
1241         g_object_unref (mail_op);
1242 }
1243
1244 void 
1245 _modest_ui_actions_on_delete_folder (GtkWidget *widget,
1246                                      ModestMainWindow *main_window)
1247 {
1248         delete_folder (main_window, FALSE);
1249 }
1250
1251 void 
1252 _modest_ui_actions_on_move_to_trash_folder (GtkWidget *widget,
1253                                             ModestMainWindow *main_window)
1254 {
1255         delete_folder (main_window, TRUE);
1256 }