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