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