e18b1ffb336e31837d8008f594a81643e9d160c8
[modest] / src / hildon2 / modest-header-window.c
1 /* Copyright (c) 2008, 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 <modest-header-window.h>
31 #include <modest-osso-state-saving.h>
32 #include <libosso.h>
33 #include <hildon/hildon-pannable-area.h>
34 #include <modest-window-mgr.h>
35 #include <modest-signal-mgr.h>
36 #include <modest-runtime.h>
37 #include <modest-platform.h>
38 #include <modest-maemo-utils.h>
39 #include <modest-icon-names.h>
40 #include <modest-ui-constants.h>
41 #include <modest-account-mgr.h>
42 #include <modest-account-mgr-helpers.h>
43 #include <modest-defs.h>
44 #include <modest-widget-memory.h>
45 #include <modest-ui-actions.h>
46 #include <modest-platform.h>
47 #include <modest-text-utils.h>
48 #include <hildon/hildon-button.h>
49 #include <hildon/hildon-program.h>
50 #include <hildon/hildon-banner.h>
51
52 typedef enum {
53         CONTENTS_STATE_NONE = 0,
54         CONTENTS_STATE_EMPTY = 1,
55         CONTENTS_STATE_HEADERS = 2
56 } ContentsState;
57
58 typedef struct _ModestHeaderWindowPrivate ModestHeaderWindowPrivate;
59 struct _ModestHeaderWindowPrivate {
60
61         GtkWidget *header_view;
62         GtkWidget *empty_view;
63         GtkWidget *contents_view;
64         GtkWidget *new_message_button;
65
66         ContentsState contents_state;
67
68         TnyFolder *folder;
69
70         /* banners */
71         GtkWidget *updating_banner;
72         guint updating_banner_timeout;
73
74         /* signals */
75         GSList *sighandlers;
76
77         /* Display state */
78         osso_display_state_t display_state;
79 };
80 #define MODEST_HEADER_WINDOW_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE((o), \
81                                                                           MODEST_TYPE_HEADER_WINDOW, \
82                                                                           ModestHeaderWindowPrivate))
83
84 /* 'private'/'protected' functions */
85 static void modest_header_window_class_init  (ModestHeaderWindowClass *klass);
86 static void modest_header_window_init        (ModestHeaderWindow *obj);
87 static void modest_header_window_finalize    (GObject *obj);
88
89 static void connect_signals (ModestHeaderWindow *self);
90 static void modest_header_window_disconnect_signals (ModestWindow *self);
91
92 static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window);
93 static void add_to_menu (ModestHeaderWindow *self,
94                          HildonAppMenu *menu,
95                          gchar *label,
96                          GCallback callback);
97 static void setup_menu (ModestHeaderWindow *self);
98 static GtkWidget *create_empty_view (void);
99 static GtkWidget *create_header_view (ModestWindow *progress_window,
100                                       TnyFolder *folder);
101
102 static void update_view (ModestHeaderWindow *self,
103                          TnyFolderChange *change);
104 static void set_contents_state (ModestHeaderWindow *window, 
105                                 ContentsState state);
106
107 static void on_msg_count_changed (ModestHeaderView *header_view,
108                                   TnyFolder *folder,
109                                   TnyFolderChange *change,
110                                   ModestHeaderWindow *header_window);
111 static void on_header_activated (ModestHeaderView *header_view,
112                                  TnyHeader *header,
113                                  GtkTreePath *path,
114                                  ModestHeaderWindow *header_window);
115 static void on_updating_msg_list (ModestHeaderView *header_view,
116                                   gboolean starting,
117                                   gpointer user_data);
118
119
120 /* globals */
121 static GtkWindowClass *parent_class = NULL;
122
123 #define EMPTYVIEW_XALIGN 0.5
124 #define EMPTYVIEW_YALIGN 0.0
125 #define EMPTYVIEW_XSPACE 1.0
126 #define EMPTYVIEW_YSPACE 0.0
127
128
129
130 /************************************************************************/
131
132 GType
133 modest_header_window_get_type (void)
134 {
135         static GType my_type = 0;
136         if (!my_type) {
137                 static const GTypeInfo my_info = {
138                         sizeof(ModestHeaderWindowClass),
139                         NULL,           /* base init */
140                         NULL,           /* base finalize */
141                         (GClassInitFunc) modest_header_window_class_init,
142                         NULL,           /* class finalize */
143                         NULL,           /* class data */
144                         sizeof(ModestHeaderWindow),
145                         1,              /* n_preallocs */
146                         (GInstanceInitFunc) modest_header_window_init,
147                         NULL
148                 };
149                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
150                                                   "ModestHeaderWindow",
151                                                   &my_info, 0);
152         }
153         return my_type;
154 }
155
156 static void
157 modest_header_window_class_init (ModestHeaderWindowClass *klass)
158 {
159         GObjectClass *gobject_class;
160         gobject_class = (GObjectClass*) klass;
161         ModestWindowClass *modest_window_class = (ModestWindowClass *) klass;
162
163         parent_class            = g_type_class_peek_parent (klass);
164         gobject_class->finalize = modest_header_window_finalize;
165
166         g_type_class_add_private (gobject_class, sizeof(ModestHeaderWindowPrivate));
167         
168         modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented;
169         modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented;
170         modest_window_class->disconnect_signals_func = modest_header_window_disconnect_signals;
171 }
172
173 static void
174 modest_header_window_init (ModestHeaderWindow *obj)
175 {
176         ModestHeaderWindowPrivate *priv;
177
178         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
179
180         priv->sighandlers = NULL;
181         priv->display_state = OSSO_DISPLAY_ON;
182         
183         priv->header_view = NULL;
184         priv->empty_view = NULL;
185         priv->contents_view = NULL;
186         priv->contents_state = CONTENTS_STATE_NONE;
187         priv->folder = NULL;
188         priv->updating_banner = NULL;
189         priv->updating_banner_timeout = 0;
190         
191         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
192                                             GTK_WINDOW(obj),
193                                             "applications_email_headerview");
194 }
195
196 static void
197 modest_header_window_finalize (GObject *obj)
198 {
199         ModestHeaderWindowPrivate *priv;
200
201         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
202
203         g_object_unref (priv->header_view);
204         g_object_unref (priv->empty_view);
205         g_object_unref (priv->folder);
206
207         /* Sanity check: shouldn't be needed, the window mgr should
208            call this function before */
209         modest_header_window_disconnect_signals (MODEST_WINDOW (obj));  
210
211         if (priv->updating_banner_timeout > 0) {
212                 g_source_remove (priv->updating_banner_timeout);
213                 priv->updating_banner_timeout = 0;
214         }
215         if (priv->updating_banner) {
216                 gtk_widget_destroy (priv->updating_banner);
217                 priv->updating_banner = NULL;
218         }
219
220         G_OBJECT_CLASS(parent_class)->finalize (obj);
221 }
222
223 static void
224 modest_header_window_disconnect_signals (ModestWindow *self)
225 {       
226         ModestHeaderWindowPrivate *priv;        
227         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
228
229         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
230         priv->sighandlers = NULL;       
231 }
232
233 static void
234 connect_signals (ModestHeaderWindow *self)
235 {       
236         ModestHeaderWindowPrivate *priv;
237         
238         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
239
240         /* header view */
241
242         priv->sighandlers = 
243                 modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), 
244                                            "msg_count_changed",
245                                            G_CALLBACK(on_msg_count_changed), self);
246         priv->sighandlers =
247                 modest_signal_mgr_connect (priv->sighandlers, G_OBJECT (priv->header_view),
248                                            "header-activated",
249                                            G_CALLBACK (on_header_activated), self);
250         priv->sighandlers = 
251                 modest_signal_mgr_connect (priv->sighandlers,
252                                            G_OBJECT (priv->header_view), 
253                                            "updating-msg-list",
254                                            G_CALLBACK (on_updating_msg_list), 
255                                            self);
256         
257         /* TODO: connect header view activate */
258
259         /* new message button */
260
261         g_signal_connect (G_OBJECT (priv->new_message_button), "clicked",
262                           G_CALLBACK (modest_ui_actions_on_new_msg), (gpointer) self);
263         
264         /* window */
265
266         /* we don't register this in sighandlers, as it should be run after disconnecting all signals,
267          * in destroy stage */
268
269         
270 }
271
272 static void 
273 osso_display_event_cb (osso_display_state_t state, 
274                        gpointer data)
275 {
276         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
277
278         priv->display_state = state;
279
280         /* Stop blinking if the screen becomes on */
281         if (priv->display_state == OSSO_DISPLAY_ON)
282                 modest_platform_remove_new_mail_notifications (TRUE);
283 }
284
285 static GtkWidget *
286 create_header_view (ModestWindow *progress_window, TnyFolder *folder)
287 {
288         GtkWidget *header_view;
289
290         header_view  = modest_header_view_new (NULL, MODEST_HEADER_VIEW_STYLE_TWOLINES);
291         modest_header_view_set_folder (MODEST_HEADER_VIEW (header_view), folder, 
292                                        TRUE, progress_window, NULL, NULL);
293         modest_widget_memory_restore (modest_runtime_get_conf (), G_OBJECT(header_view),
294                                       MODEST_CONF_HEADER_VIEW_KEY);
295
296         return header_view;
297 }
298
299 static GtkWidget *
300 create_empty_view (void)
301 {
302         GtkLabel *label = NULL;
303         GtkWidget *align = NULL;
304
305         align = gtk_alignment_new(EMPTYVIEW_XALIGN, EMPTYVIEW_YALIGN, EMPTYVIEW_XSPACE, EMPTYVIEW_YSPACE);
306         label = GTK_LABEL(gtk_label_new (_("mcen_ia_nomessages")));
307         gtk_label_set_justify (label, GTK_JUSTIFY_CENTER);      
308         gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET(label));
309
310         return GTK_WIDGET(align);
311 }
312
313
314 ModestWindow *
315 modest_header_window_new (TnyFolder *folder)
316 {
317         ModestHeaderWindow *self = NULL;        
318         ModestHeaderWindowPrivate *priv = NULL;
319         HildonProgram *app;
320         GdkPixbuf *window_icon;
321         GtkWidget *pannable;
322         
323         self  = MODEST_HEADER_WINDOW(g_object_new(MODEST_TYPE_HEADER_WINDOW, NULL));
324         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
325
326         priv->folder = g_object_ref (folder);
327
328         pannable = hildon_pannable_area_new ();
329
330         priv->header_view  = create_header_view (MODEST_WINDOW (self), folder);
331         priv->empty_view = create_empty_view ();
332         g_object_ref (priv->header_view);
333         g_object_ref (priv->empty_view);
334         priv->contents_view = gtk_vbox_new (FALSE, 0);
335         priv->new_message_button = hildon_button_new (MODEST_EDITABLE_SIZE,
336                                                       HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
337         hildon_button_set_title (HILDON_BUTTON (priv->new_message_button), _("mcen_me_viewer_newemail"));
338         hildon_button_set_image (HILDON_BUTTON (priv->new_message_button), 
339                                  gtk_image_new_from_stock (MODEST_STOCK_NEW_MAIL, HILDON_ICON_SIZE_TOOLBAR));
340         hildon_button_set_title_alignment (HILDON_BUTTON (priv->new_message_button), 0.0, 0.5);
341         hildon_button_set_image_alignment (HILDON_BUTTON (priv->new_message_button), 0.0, 0.5);
342         hildon_button_set_alignment (HILDON_BUTTON (priv->new_message_button), 0.0, 0.5, 1.0, 0.0);
343         hildon_button_set_image_position (HILDON_BUTTON (priv->new_message_button), GTK_POS_LEFT);
344
345         setup_menu (self);
346
347         gtk_box_pack_start (GTK_BOX (priv->contents_view), priv->new_message_button, FALSE, FALSE, 0);
348         hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (pannable), priv->contents_view);
349         gtk_container_add (GTK_CONTAINER (self), pannable);
350
351         gtk_widget_show (priv->contents_view);
352         gtk_widget_show (pannable);
353         gtk_widget_show (priv->new_message_button);
354
355         connect_signals (MODEST_HEADER_WINDOW (self));
356
357         update_view (self, NULL);
358
359         /* Load previous osso state, for instance if we are being restored from 
360          * hibernation:  */
361         modest_osso_load_state ();
362
363         /* Get device name */
364         modest_maemo_utils_get_device_name ();
365
366         app = hildon_program_get_instance ();
367         hildon_program_add_window (app, HILDON_WINDOW (self));
368         
369         /* Set window icon */
370         window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG);
371         if (window_icon) {
372                 gtk_window_set_icon (GTK_WINDOW (self), window_icon);
373                 g_object_unref (window_icon);
374         }
375
376         /* Listen for changes in the screen, we don't want to show a
377            led pattern when the display is on for example */
378         osso_hw_set_display_event_cb (modest_maemo_utils_get_osso_context (),
379                                       osso_display_event_cb,
380                                       self); 
381
382         /* Dont't restore settings here, 
383          * because it requires a gtk_widget_show(), 
384          * and we don't want to do that until later,
385          * so that the UI is not visible for non-menu D-Bus activation.
386          */
387
388         return MODEST_WINDOW(self);
389 }
390
391 static gboolean
392 on_zoom_minus_plus_not_implemented (ModestWindow *window)
393 {
394         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (window), FALSE);
395
396         hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
397         return FALSE;
398
399 }
400
401 gboolean
402 modest_header_window_screen_is_on (ModestHeaderWindow *self)
403 {
404         ModestHeaderWindowPrivate *priv = NULL;
405
406         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
407
408         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
409         
410         return (priv->display_state == OSSO_DISPLAY_ON) ? TRUE : FALSE;
411 }
412
413 ModestHeaderView *
414 modest_header_window_get_header_view (ModestHeaderWindow *self)
415 {
416         ModestHeaderWindowPrivate *priv = NULL;
417
418         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
419
420         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
421         
422         return MODEST_HEADER_VIEW (priv->header_view);
423 }
424
425 static void add_to_menu (ModestHeaderWindow *self,
426                          HildonAppMenu *menu,
427                          gchar *label,
428                          GCallback callback)
429 {
430         GtkWidget *button;
431
432         button = gtk_button_new_with_label (label);
433         g_signal_connect_after (G_OBJECT (button), "clicked",
434                                 callback, (gpointer) self);
435         hildon_app_menu_append (menu, GTK_BUTTON (button));
436 }
437
438 static void setup_menu (ModestHeaderWindow *self)
439 {
440         ModestHeaderWindowPrivate *priv = NULL;
441         GtkWidget *app_menu;
442
443         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
444
445         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
446
447         app_menu = hildon_app_menu_new ();
448
449         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
450                      G_CALLBACK (modest_ui_actions_on_new_msg));
451         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_sendandreceive"),
452                      G_CALLBACK (modest_ui_actions_on_send_receive));
453         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_messagedetails"),
454                      G_CALLBACK (modest_ui_actions_on_details));
455         add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_sort"),
456                      G_CALLBACK (modest_ui_actions_on_sort));
457
458         hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self), 
459                                                HILDON_APP_MENU (app_menu));
460 }
461
462 static void 
463 update_view (ModestHeaderWindow *self,
464              TnyFolderChange *change)
465 {
466         ModestHeaderWindowPrivate *priv = NULL;
467         gboolean refilter = FALSE;
468         gboolean folder_empty = FALSE;
469         gboolean all_marked_as_deleted = FALSE;
470
471         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
472         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
473         g_return_if_fail (priv->folder);
474
475         if (change != NULL) {
476                 TnyFolderChangeChanged changed;
477
478                 changed = tny_folder_change_get_changed (change);
479                 /* If something changes */
480                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
481                         folder_empty = (((guint) tny_folder_change_get_new_all_count (change)) == 0);
482                 else
483                         folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
484
485                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
486                         refilter = TRUE;
487         } else {
488                 folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
489         }
490
491         /* Check if all messages are marked to be deleted */
492         all_marked_as_deleted = modest_header_view_is_empty (MODEST_HEADER_VIEW (priv->header_view));
493         folder_empty = folder_empty || all_marked_as_deleted;
494
495         /* Set style of headers view */
496         set_contents_state (self, folder_empty?CONTENTS_STATE_EMPTY:CONTENTS_STATE_HEADERS);
497
498         if (refilter)
499                 modest_header_view_refilter (MODEST_HEADER_VIEW (priv->header_view));
500 }
501
502 static void 
503 set_contents_state (ModestHeaderWindow *self, 
504                     ContentsState state)
505 {
506         ModestHeaderWindowPrivate *priv = NULL;
507
508         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
509         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
510
511         if (priv->contents_state == state)
512                 return;
513
514         /* Remove from container the old content */
515         switch (priv->contents_state) {
516         case CONTENTS_STATE_EMPTY:
517                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->empty_view);
518                 break;
519         case CONTENTS_STATE_HEADERS:
520                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->header_view);
521                 break;
522         case CONTENTS_STATE_NONE:
523                 break;
524         }
525
526         /* Add the new content */
527         switch (state) {
528         case CONTENTS_STATE_EMPTY:
529                 gtk_box_pack_start (GTK_BOX (priv->contents_view), priv->empty_view, TRUE, TRUE, 0);
530                 gtk_widget_show (priv->empty_view);
531                 break;
532         case CONTENTS_STATE_HEADERS:
533                 gtk_box_pack_start (GTK_BOX (priv->contents_view), priv->header_view, TRUE, TRUE, 0);
534                 gtk_widget_show (priv->header_view);
535                 break;
536         case CONTENTS_STATE_NONE:
537                 break;
538         }
539         priv->contents_state = state;
540         
541 }
542
543 static void
544 on_msg_count_changed (ModestHeaderView *header_view,
545                       TnyFolder *folder,
546                       TnyFolderChange *change,
547                       ModestHeaderWindow *header_window)
548 {       
549         g_return_if_fail (MODEST_IS_HEADER_WINDOW (header_window));
550         
551         update_view (MODEST_HEADER_WINDOW (header_window), change);
552 }
553
554 static void 
555 on_header_activated (ModestHeaderView *header_view,
556                      TnyHeader *header,
557                      GtkTreePath *path,
558                      ModestHeaderWindow *header_window)
559 {
560         modest_ui_actions_on_header_activated (header_view, header, path, MODEST_WINDOW (header_window));
561 }
562
563 static void
564 updating_banner_destroyed (gpointer data,
565                            GObject *where_the_object_was)
566 {
567         ModestHeaderWindowPrivate *priv = NULL;
568
569         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
570
571         priv->updating_banner = NULL;
572 }
573
574 static gboolean
575 show_updating_banner (gpointer user_data)
576 {
577         ModestHeaderWindowPrivate *priv = NULL;
578
579         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
580
581         if (priv->updating_banner == NULL) {
582
583                 /* We're outside the main lock */
584                 gdk_threads_enter ();
585                 priv->updating_banner = 
586                         modest_platform_animation_banner (GTK_WIDGET (user_data), NULL,
587                                                           _CS ("ckdg_pb_updating"));
588
589                 /* We need this because banners in Maemo could be
590                    destroyed by dialogs so we need to properly update
591                    our reference to it */
592                 g_object_weak_ref (G_OBJECT (priv->updating_banner),
593                                    updating_banner_destroyed,
594                                    user_data);
595                 gdk_threads_leave ();
596         }
597
598         /* Remove timeout */
599         priv->updating_banner_timeout = 0;
600         return FALSE;
601 }
602
603 /**
604  * We use this function to show/hide a progress banner showing
605  * "Updating" while the header view is being filled. We're not showing
606  * it unless the update takes more than 2 seconds
607  *
608  * If starting = TRUE then the refresh is starting, otherwise it means
609  * that is has just finished
610  */
611 static void 
612 on_updating_msg_list (ModestHeaderView *header_view,
613                       gboolean starting,
614                       gpointer user_data)
615 {
616         ModestHeaderWindowPrivate *priv = NULL;
617
618         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
619         
620         /* Remove old timeout */
621         if (priv->updating_banner_timeout > 0) {
622                 g_source_remove (priv->updating_banner_timeout);
623                 priv->updating_banner_timeout = 0;
624         }
625
626         /* Create a new timeout */
627         if (starting) {
628                 priv->updating_banner_timeout = 
629                         g_timeout_add (2000, show_updating_banner, user_data);
630         } else {
631                 /* Remove the banner if exists */
632                 if (priv->updating_banner) {
633                         gtk_widget_destroy (priv->updating_banner);
634                         priv->updating_banner = NULL;
635                 }
636         }
637 }