Fixes several reference leaks that were preventing the folder view window from being...
[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-window-priv.h>
36 #include <modest-signal-mgr.h>
37 #include <modest-runtime.h>
38 #include <modest-platform.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-icon-names.h>
41 #include <modest-ui-constants.h>
42 #include <modest-account-mgr.h>
43 #include <modest-account-mgr-helpers.h>
44 #include <modest-defs.h>
45 #include <modest-widget-memory.h>
46 #include <modest-ui-actions.h>
47 #include <modest-platform.h>
48 #include <modest-text-utils.h>
49 #include <hildon/hildon-button.h>
50 #include <hildon/hildon-program.h>
51 #include <hildon/hildon-banner.h>
52 #include <modest-ui-dimming-rules.h>
53 #include <modest-tny-folder.h>
54
55 typedef enum {
56         CONTENTS_STATE_NONE = 0,
57         CONTENTS_STATE_EMPTY = 1,
58         CONTENTS_STATE_HEADERS = 2
59 } ContentsState;
60
61 typedef enum {
62         EDIT_MODE_COMMAND_MOVE = 1,
63         EDIT_MODE_COMMAND_DELETE = 2,
64 } EditModeCommand;
65
66 typedef struct _ModestHeaderWindowPrivate ModestHeaderWindowPrivate;
67 struct _ModestHeaderWindowPrivate {
68
69         GtkWidget *header_view;
70         GtkWidget *empty_view;
71         GtkWidget *contents_view;
72         GtkWidget *top_vbox;
73
74         /* state bar */
75         ContentsState contents_state;
76
77         TnyFolder *folder;
78
79         /* autoscroll */
80         gboolean autoscroll;
81
82         /* banners */
83         GtkWidget *updating_banner;
84         guint updating_banner_timeout;
85
86         /* signals */
87         GSList *sighandlers;
88         gulong queue_change_handler;
89
90         /* progress hint */
91         gboolean progress_hint;
92         gchar *current_store_account;
93
94         /* sort button */
95         GtkWidget *sort_button;
96 };
97 #define MODEST_HEADER_WINDOW_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE((o), \
98                                                                           MODEST_TYPE_HEADER_WINDOW, \
99                                                                           ModestHeaderWindowPrivate))
100
101 /* 'private'/'protected' functions */
102 static void modest_header_window_class_init  (ModestHeaderWindowClass *klass);
103 static void modest_header_window_init        (ModestHeaderWindow *obj);
104 static void modest_header_window_finalize    (GObject *obj);
105
106 static void connect_signals (ModestHeaderWindow *self);
107 static void modest_header_window_disconnect_signals (ModestWindow *self);
108
109 static void setup_menu (ModestHeaderWindow *self);
110 static GtkWidget *create_empty_view (void);
111 static GtkWidget *create_header_view (ModestWindow *progress_window,
112                                       TnyFolder *folder);
113
114 static void update_view (ModestHeaderWindow *self,
115                          TnyFolderChange *change);
116 static void set_contents_state (ModestHeaderWindow *window, 
117                                 ContentsState state);
118
119 static void on_msg_count_changed (ModestHeaderView *header_view,
120                                   TnyFolder *folder,
121                                   TnyFolderChange *change,
122                                   ModestHeaderWindow *header_window);
123 static void on_header_activated (ModestHeaderView *header_view,
124                                  TnyHeader *header,
125                                  GtkTreePath *path,
126                                  ModestHeaderWindow *header_window);
127 static void on_updating_msg_list (ModestHeaderView *header_view,
128                                   gboolean starting,
129                                   gpointer user_data);
130 static void set_delete_edit_mode (GtkButton *button,
131                                   ModestHeaderWindow *self);
132 static void set_moveto_edit_mode (GtkButton *button,
133                                   ModestHeaderWindow *self);
134 static gboolean on_expose_event(GtkTreeView *header_view,
135                                 GdkEventExpose *event,
136                                 gpointer user_data);
137 static gboolean on_map_event (GtkWidget *widget,
138                               GdkEvent *event,
139                               gpointer userdata);
140 static void on_vertical_movement (HildonPannableArea *area,
141                                   HildonMovementDirection direction,
142                                   gdouble x, gdouble y, gpointer user_data);
143 static void on_queue_changed    (ModestMailOperationQueue *queue,
144                                  ModestMailOperation *mail_op,
145                                  ModestMailOperationQueueNotification type,
146                                  ModestHeaderWindow *self);
147 static void modest_header_window_pack_toolbar (ModestHildon2Window *self,
148                                                GtkPackType pack_type,
149                                                GtkWidget *toolbar);
150 static void edit_mode_changed (ModestHeaderWindow *header_window,
151                                gint edit_mode_id,
152                                gboolean enabled,
153                                ModestHeaderWindow *self);
154 static void on_progress_list_changed (ModestWindowMgr *mgr,
155                                       ModestHeaderWindow *self);
156 static void update_progress_hint (ModestHeaderWindow *self);
157 static void on_sort_column_changed (GtkTreeSortable *treesortable,
158                                     gpointer         user_data);
159 static void update_sort_button (ModestHeaderWindow *self);
160
161
162 /* globals */
163 static GtkWindowClass *parent_class = NULL;
164
165 #define EMPTYVIEW_XALIGN 0.5
166 #define EMPTYVIEW_YALIGN 0.0
167 #define EMPTYVIEW_XSPACE 1.0
168 #define EMPTYVIEW_YSPACE 0.0
169
170
171
172 /************************************************************************/
173
174 GType
175 modest_header_window_get_type (void)
176 {
177         static GType my_type = 0;
178         if (!my_type) {
179                 static const GTypeInfo my_info = {
180                         sizeof(ModestHeaderWindowClass),
181                         NULL,           /* base init */
182                         NULL,           /* base finalize */
183                         (GClassInitFunc) modest_header_window_class_init,
184                         NULL,           /* class finalize */
185                         NULL,           /* class data */
186                         sizeof(ModestHeaderWindow),
187                         1,              /* n_preallocs */
188                         (GInstanceInitFunc) modest_header_window_init,
189                         NULL
190                 };
191                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
192                                                   "ModestHeaderWindow",
193                                                   &my_info, 0);
194         }
195         return my_type;
196 }
197
198 static void
199 modest_header_window_class_init (ModestHeaderWindowClass *klass)
200 {
201         GObjectClass *gobject_class;
202         gobject_class = (GObjectClass*) klass;
203         ModestWindowClass *modest_window_class = (ModestWindowClass *) klass;
204         ModestHildon2WindowClass *modest_hildon2_window_class = (ModestHildon2WindowClass *) klass;
205
206         parent_class            = g_type_class_peek_parent (klass);
207         gobject_class->finalize = modest_header_window_finalize;
208
209         g_type_class_add_private (gobject_class, sizeof(ModestHeaderWindowPrivate));
210         
211         modest_window_class->disconnect_signals_func = modest_header_window_disconnect_signals;
212         modest_hildon2_window_class->pack_toolbar_func = modest_header_window_pack_toolbar;
213 }
214
215 static void
216 modest_header_window_init (ModestHeaderWindow *obj)
217 {
218         ModestHeaderWindowPrivate *priv;
219
220         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
221
222         priv->sighandlers = NULL;
223         
224         priv->header_view = NULL;
225         priv->empty_view = NULL;
226         priv->top_vbox = NULL;
227         priv->contents_view = NULL;
228         priv->contents_state = CONTENTS_STATE_NONE;
229         priv->folder = NULL;
230         priv->updating_banner = NULL;
231         priv->updating_banner_timeout = 0;
232         priv->autoscroll = TRUE;
233         priv->progress_hint = FALSE;
234         priv->queue_change_handler = 0;
235         priv->current_store_account = NULL;
236         priv->sort_button = NULL;
237         
238         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
239                                             GTK_WINDOW(obj),
240                                             "applications_email_headerview");
241 }
242
243 static void
244 modest_header_window_finalize (GObject *obj)
245 {
246         ModestHeaderWindowPrivate *priv;
247
248         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
249
250         g_object_unref (priv->folder);
251
252         if (priv->current_store_account) {
253                 g_free (priv->current_store_account);
254                 priv->current_store_account = NULL;
255         }
256
257         /* Sanity check: shouldn't be needed, the window mgr should
258            call this function before */
259         modest_header_window_disconnect_signals (MODEST_WINDOW (obj));  
260
261         if (priv->updating_banner_timeout > 0) {
262                 g_source_remove (priv->updating_banner_timeout);
263                 priv->updating_banner_timeout = 0;
264         }
265         if (priv->updating_banner) {
266                 gtk_widget_destroy (priv->updating_banner);
267                 priv->updating_banner = NULL;
268         }
269
270         G_OBJECT_CLASS(parent_class)->finalize (obj);
271 }
272
273 static void
274 modest_header_window_disconnect_signals (ModestWindow *self)
275 {
276         ModestHeaderWindowPrivate *priv;
277         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
278
279         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
280                                            priv->queue_change_handler))
281                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
282                                              priv->queue_change_handler);
283
284         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
285         priv->sighandlers = NULL;
286
287 }
288
289 static void
290 connect_signals (ModestHeaderWindow *self)
291 {       
292         ModestHeaderWindowPrivate *priv;
293         GtkTreeSortable *sortable;
294         
295         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
296
297         /* header view */
298
299         priv->sighandlers = 
300                 modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), 
301                                            "msg_count_changed",
302                                            G_CALLBACK(on_msg_count_changed), self);
303         priv->sighandlers =
304                 modest_signal_mgr_connect (priv->sighandlers, G_OBJECT (priv->header_view),
305                                            "header-activated",
306                                            G_CALLBACK (on_header_activated), self);
307         priv->sighandlers = 
308                 modest_signal_mgr_connect (priv->sighandlers,
309                                            G_OBJECT (priv->header_view), 
310                                            "updating-msg-list",
311                                            G_CALLBACK (on_updating_msg_list), 
312                                            self);
313         priv->sighandlers =
314                 modest_signal_mgr_connect (priv->sighandlers,
315                                            G_OBJECT (priv->header_view),
316                                            "expose-event",
317                                            G_CALLBACK (on_expose_event),
318                                            self);
319
320         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
321                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (
322                                                                       GTK_TREE_VIEW (priv->header_view)))));
323         priv->sighandlers = 
324                 modest_signal_mgr_connect (priv->sighandlers,
325                                            G_OBJECT (sortable),
326                                            "sort-column-changed",
327                                            G_CALLBACK (on_sort_column_changed),
328                                            self);
329
330         priv->sighandlers =
331                 modest_signal_mgr_connect (priv->sighandlers,
332                                            G_OBJECT (self),
333                                            "map-event",
334                                            G_CALLBACK (on_map_event),
335                                            self);
336
337         priv->sighandlers =
338                 modest_signal_mgr_connect (priv->sighandlers,
339                                            G_OBJECT (priv->contents_view), 
340                                            "vertical-movement", 
341                                            G_CALLBACK (on_vertical_movement), 
342                                            self);
343
344         /* Mail Operation Queue */
345         priv->queue_change_handler =
346                 g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
347                                   "queue-changed",
348                                   G_CALLBACK (on_queue_changed),
349                                   self);
350         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
351                                                        G_OBJECT (modest_runtime_get_window_mgr ()),
352                                                        "progress-list-changed",
353                                                        G_CALLBACK (on_progress_list_changed), self);
354 }
355
356 static GtkWidget *
357 create_header_view (ModestWindow *self, TnyFolder *folder)
358 {
359         GtkWidget *header_view;
360
361         header_view  = modest_header_view_new (NULL, MODEST_HEADER_VIEW_STYLE_TWOLINES);
362         modest_header_view_set_folder (MODEST_HEADER_VIEW (header_view), folder, 
363                                        TRUE, self, NULL, NULL);
364         modest_header_view_set_filter (MODEST_HEADER_VIEW (header_view), 
365                                        MODEST_HEADER_VIEW_FILTER_NONE);
366         modest_widget_memory_restore (modest_runtime_get_conf (), G_OBJECT(header_view),
367                                       MODEST_CONF_HEADER_VIEW_KEY);
368
369         return header_view;
370 }
371
372 static GtkWidget *
373 create_empty_view (void)
374 {
375         GtkWidget *label = NULL;
376         GtkWidget *align = NULL;
377
378         align = gtk_alignment_new(EMPTYVIEW_XALIGN, EMPTYVIEW_YALIGN, EMPTYVIEW_XSPACE, EMPTYVIEW_YSPACE);
379         label = gtk_label_new (_("mcen_ia_nomessages"));
380         gtk_widget_show (label);
381         gtk_widget_show (align);
382         gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);  
383         gtk_container_add (GTK_CONTAINER (align), label);
384
385         return align;
386 }
387
388 static void
389 on_vertical_movement (HildonPannableArea *area,
390                       HildonMovementDirection direction,
391                       gdouble x, gdouble y, gpointer user_data)
392 {
393         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
394         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
395
396         priv->autoscroll = FALSE;
397 }
398
399
400 ModestWindow *
401 modest_header_window_new (TnyFolder *folder, const gchar *account_name)
402 {
403         ModestHeaderWindow *self = NULL;        
404         ModestHeaderWindowPrivate *priv = NULL;
405         HildonProgram *app;
406         GdkPixbuf *window_icon;
407         ModestAccountMgr *mgr;
408         ModestAccountSettings *settings = NULL;
409         ModestServerAccountSettings *store_settings = NULL;
410         
411         self  = MODEST_HEADER_WINDOW(g_object_new(MODEST_TYPE_HEADER_WINDOW, NULL));
412         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
413
414         priv->folder = g_object_ref (folder);
415
416         priv->contents_view = hildon_pannable_area_new ();
417
418         priv->header_view  = create_header_view (MODEST_WINDOW (self), folder);
419         priv->empty_view = create_empty_view ();
420         g_signal_connect (G_OBJECT (self), "edit-mode-changed",
421                           G_CALLBACK (edit_mode_changed), (gpointer) self);
422         setup_menu (self);
423
424         priv->top_vbox = gtk_vbox_new (FALSE, 0);
425         gtk_box_pack_end (GTK_BOX (priv->top_vbox), priv->contents_view, TRUE, TRUE, 0);
426
427         gtk_container_add (GTK_CONTAINER (self), priv->top_vbox);
428
429         gtk_widget_show (priv->contents_view);
430         gtk_widget_show (priv->top_vbox);
431
432         connect_signals (MODEST_HEADER_WINDOW (self));
433
434         update_view (self, NULL);
435
436         /* Load previous osso state, for instance if we are being restored from 
437          * hibernation:  */
438         modest_osso_load_state ();
439
440         /* Get device name */
441         modest_maemo_utils_get_device_name ();
442
443         app = hildon_program_get_instance ();
444         hildon_program_add_window (app, HILDON_WINDOW (self));
445
446         /* Set window icon */
447         window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG);
448         if (window_icon) {
449                 gtk_window_set_icon (GTK_WINDOW (self), window_icon);
450                 g_object_unref (window_icon);
451         }
452
453         /* Set window title */
454         if (TNY_IS_FOLDER (folder)) {
455                 gchar *folder_name;
456
457                 folder_name = modest_tny_folder_get_display_name (folder);
458                 gtk_window_set_title (GTK_WINDOW (self), folder_name);
459                 g_free (folder_name);
460         }
461
462         /* Dont't restore settings here, 
463          * because it requires a gtk_widget_show(), 
464          * and we don't want to do that until later,
465          * so that the UI is not visible for non-menu D-Bus activation.
466          */
467
468         /* setup edit modes */
469         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE,
470                                                   _("mcen_ti_edit_delete"), _HL("wdgt_bd_delete"),
471                                                   GTK_TREE_VIEW (priv->header_view),
472                                                   GTK_SELECTION_MULTIPLE,
473                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_delete_message));
474         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE,
475                                                   _("mcen_ti_edit_move"), _HL("wdgt_bd_move"),
476                                                   GTK_TREE_VIEW (priv->header_view),
477                                                   GTK_SELECTION_MULTIPLE,
478                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_move_to));
479
480
481         modest_window_set_active_account (MODEST_WINDOW (self), account_name);
482         mgr = modest_runtime_get_account_mgr ();
483         settings = modest_account_mgr_load_account_settings (mgr, account_name);
484         if (settings) {
485                 store_settings = modest_account_settings_get_store_settings (settings);
486                 if (store_settings) {
487                         priv->current_store_account = 
488                                 g_strdup (modest_server_account_settings_get_account_name (store_settings));
489                         g_object_unref (store_settings);
490                 }
491                 g_object_unref (settings);
492         }
493
494         update_progress_hint (self);
495         update_sort_button (self);
496
497         return MODEST_WINDOW(self);
498 }
499
500 ModestHeaderView *
501 modest_header_window_get_header_view (ModestHeaderWindow *self)
502 {
503         ModestHeaderWindowPrivate *priv = NULL;
504
505         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
506
507         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
508         
509         return MODEST_HEADER_VIEW (priv->header_view);
510 }
511
512 static void setup_menu (ModestHeaderWindow *self)
513 {
514         ModestHeaderWindowPrivate *priv;
515
516         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
517         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
518
519         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
520                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
521                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
522         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_messages"), NULL,
523                                            APP_MENU_CALLBACK (set_moveto_edit_mode),
524                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
525         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_delete_messages"), NULL,
526                                            APP_MENU_CALLBACK (set_delete_edit_mode),
527                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_delete));
528         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_folder_details"), NULL,
529                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
530                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
531         priv->sort_button = hildon_button_new (MODEST_EDITABLE_SIZE,
532                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL);
533         hildon_button_set_title (HILDON_BUTTON (priv->sort_button), _("mcen_me_sort"));
534         g_signal_connect (G_OBJECT (priv->sort_button), "clicked",
535                           G_CALLBACK (modest_ui_actions_on_sort), (gpointer) self);
536         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->sort_button),
537                                                   modest_ui_dimming_rules_on_sort);
538         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_sendandreceive"), NULL,
539                                            APP_MENU_CALLBACK (modest_ui_actions_on_send_receive),
540                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_send_receive));
541         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_outbox_cancelsend"), NULL,
542                                            APP_MENU_CALLBACK (modest_ui_actions_cancel_send),
543                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_cancel_sending_all));
544 }
545
546 static void 
547 update_view (ModestHeaderWindow *self,
548              TnyFolderChange *change)
549 {
550         ModestHeaderWindowPrivate *priv = NULL;
551         gboolean refilter = FALSE;
552         gboolean folder_empty = FALSE;
553         gboolean all_marked_as_deleted = FALSE;
554
555         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
556         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
557         g_return_if_fail (priv->folder);
558
559         if (change != NULL) {
560                 TnyFolderChangeChanged changed;
561
562                 changed = tny_folder_change_get_changed (change);
563                 /* If something changes */
564                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
565                         folder_empty = (((guint) tny_folder_change_get_new_all_count (change)) == 0);
566                 else
567                         folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
568
569                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
570                         refilter = TRUE;
571         } else {
572                 folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
573         }
574
575         /* Check if all messages are marked to be deleted */
576         all_marked_as_deleted = modest_header_view_is_empty (MODEST_HEADER_VIEW (priv->header_view));
577         folder_empty = folder_empty || all_marked_as_deleted;
578
579         /* Set style of headers view */
580         set_contents_state (self, folder_empty?CONTENTS_STATE_EMPTY:CONTENTS_STATE_HEADERS);
581
582         if (refilter)
583                 modest_header_view_refilter (MODEST_HEADER_VIEW (priv->header_view));
584 }
585
586 static void 
587 set_contents_state (ModestHeaderWindow *self, 
588                     ContentsState state)
589 {
590         ModestHeaderWindowPrivate *priv = NULL;
591
592         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
593         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
594
595         if (priv->contents_state == state)
596                 return;
597
598         /* Remove from container the old content */
599         switch (priv->contents_state) {
600         case CONTENTS_STATE_EMPTY:
601                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->empty_view);
602                 break;
603         case CONTENTS_STATE_HEADERS:
604                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->header_view);
605                 break;
606         case CONTENTS_STATE_NONE:
607                 break;
608         }
609
610         /* Add the new content */
611         switch (state) {
612         case CONTENTS_STATE_EMPTY:
613                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->empty_view);
614                 gtk_widget_show (priv->empty_view);
615                 break;
616         case CONTENTS_STATE_HEADERS:
617                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->header_view);
618                 gtk_widget_show (priv->header_view);
619                 break;
620         case CONTENTS_STATE_NONE:
621                 break;
622         }
623         priv->contents_state = state;
624         
625 }
626
627 static void
628 on_msg_count_changed (ModestHeaderView *header_view,
629                       TnyFolder *folder,
630                       TnyFolderChange *change,
631                       ModestHeaderWindow *header_window)
632 {       
633         g_return_if_fail (MODEST_IS_HEADER_WINDOW (header_window));
634         
635         update_view (MODEST_HEADER_WINDOW (header_window), change);
636 }
637
638 static void 
639 on_header_activated (ModestHeaderView *header_view,
640                      TnyHeader *header,
641                      GtkTreePath *path,
642                      ModestHeaderWindow *header_window)
643 {
644         modest_ui_actions_on_header_activated (header_view, header, path, MODEST_WINDOW (header_window));
645 }
646
647 static void
648 updating_banner_destroyed (gpointer data,
649                            GObject *where_the_object_was)
650 {
651         ModestHeaderWindowPrivate *priv = NULL;
652
653         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
654
655         priv->updating_banner = NULL;
656 }
657
658 static gboolean
659 show_updating_banner (gpointer user_data)
660 {
661         ModestHeaderWindowPrivate *priv = NULL;
662
663         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
664
665         if (priv->updating_banner == NULL) {
666
667                 /* We're outside the main lock */
668                 gdk_threads_enter ();
669                 priv->updating_banner = 
670                         modest_platform_animation_banner (GTK_WIDGET (user_data), NULL,
671                                                           _CS ("ckdg_pb_updating"));
672
673                 /* We need this because banners in Maemo could be
674                    destroyed by dialogs so we need to properly update
675                    our reference to it */
676                 g_object_weak_ref (G_OBJECT (priv->updating_banner),
677                                    updating_banner_destroyed,
678                                    user_data);
679                 gdk_threads_leave ();
680         }
681
682         /* Remove timeout */
683         priv->updating_banner_timeout = 0;
684         return FALSE;
685 }
686
687 /**
688  * We use this function to show/hide a progress banner showing
689  * "Updating" while the header view is being filled. We're not showing
690  * it unless the update takes more than 2 seconds
691  *
692  * If starting = TRUE then the refresh is starting, otherwise it means
693  * that is has just finished
694  */
695 static void 
696 on_updating_msg_list (ModestHeaderView *header_view,
697                       gboolean starting,
698                       gpointer user_data)
699 {
700         ModestHeaderWindowPrivate *priv = NULL;
701
702         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
703         
704         /* Remove old timeout */
705         if (priv->updating_banner_timeout > 0) {
706                 g_source_remove (priv->updating_banner_timeout);
707                 priv->updating_banner_timeout = 0;
708         }
709
710         /* Create a new timeout */
711         if (starting) {
712                 priv->updating_banner_timeout = 
713                         g_timeout_add (2000, show_updating_banner, user_data);
714         } else {
715                 /* Remove the banner if exists */
716                 if (priv->updating_banner) {
717                         gtk_widget_destroy (priv->updating_banner);
718                         priv->updating_banner = NULL;
719                 }
720         }
721 }
722
723 static void
724 set_delete_edit_mode (GtkButton *button,
725                       ModestHeaderWindow *self)
726 {
727         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE);
728 }
729
730 static void
731 set_moveto_edit_mode (GtkButton *button,
732                     ModestHeaderWindow *self)
733 {
734         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE);
735 }
736
737 static gboolean 
738 on_expose_event(GtkTreeView *header_view,
739                 GdkEventExpose *event,
740                 gpointer user_data)
741 {
742         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
743         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
744
745         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
746
747         if (priv->autoscroll)
748                 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (priv->contents_view), 0.0, 0.0);
749
750         return FALSE;
751 }
752
753 static gboolean 
754 on_map_event(GtkWidget *widget,
755              GdkEvent *event,
756              gpointer user_data)
757 {
758         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
759         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
760
761         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
762
763         if (priv->progress_hint) {
764                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
765         }
766         return FALSE;
767 }
768
769 static void
770 on_progress_list_changed (ModestWindowMgr *mgr,
771                           ModestHeaderWindow *self)
772 {
773         update_progress_hint (self);
774 }
775
776 static gboolean
777 has_active_operations (ModestHeaderWindow *self)
778 {
779         GSList *operations = NULL, *node;
780         ModestMailOperationQueue *queue;
781         gboolean has_active = FALSE;
782
783         queue = modest_runtime_get_mail_operation_queue ();
784         operations = modest_mail_operation_queue_get_by_source (queue, G_OBJECT (self));
785
786         for (node = operations; node != NULL; node = g_slist_next (node)) {
787                 if (!modest_mail_operation_is_finished (MODEST_MAIL_OPERATION (node->data))) {
788                         has_active = TRUE;
789                         break;
790                 }
791         }
792
793         if (operations) {
794                 g_slist_foreach (operations, (GFunc) g_object_unref, NULL);
795                 g_slist_free (operations);
796         }
797
798         return has_active;
799 }
800
801 static void
802 update_progress_hint (ModestHeaderWindow *self)
803 {
804         ModestHeaderWindowPrivate *priv;
805
806         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
807
808         priv->progress_hint = FALSE;
809
810         if (has_active_operations (self)) {
811                 priv->progress_hint = TRUE;
812         }
813
814         return;
815
816         if (!priv->progress_hint && priv->current_store_account) {
817                 priv->progress_hint = 
818                         modest_window_mgr_has_progress_operation_on_account (modest_runtime_get_window_mgr (),
819                                                                              priv->current_store_account);
820         }
821
822         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
823
824         if (GTK_WIDGET_VISIBLE (self)) {
825                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), priv->progress_hint?1:0);
826         }
827 }
828
829 gboolean
830 modest_header_window_toolbar_on_transfer_mode     (ModestHeaderWindow *self)
831 {
832         ModestHeaderWindowPrivate *priv= NULL; 
833
834         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
835         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
836
837         return priv->progress_hint;
838 }
839
840 gboolean 
841 modest_header_window_transfer_mode_enabled (ModestHeaderWindow *self)
842 {
843         ModestHeaderWindowPrivate *priv;
844         
845         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);   
846         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
847
848         return priv->progress_hint;
849 }
850
851 static void 
852 on_mail_operation_started (ModestMailOperation *mail_op,
853                            gpointer user_data)
854 {
855         ModestHeaderWindow *self;
856         ModestMailOperationTypeOperation op_type;
857         GObject *source = NULL;
858
859         self = MODEST_HEADER_WINDOW (user_data);
860         op_type = modest_mail_operation_get_type_operation (mail_op);
861         source = modest_mail_operation_get_source(mail_op);
862         if (G_OBJECT (self) == source) {
863                 update_progress_hint (self);
864         }
865         g_object_unref (source);
866 }
867
868 static void 
869 on_mail_operation_finished (ModestMailOperation *mail_op,
870                             gpointer user_data)
871 {
872         ModestHeaderWindow *self;
873
874         self = MODEST_HEADER_WINDOW (user_data);
875
876         /* Don't disable the progress hint if there are more pending
877            operations from this window */
878         update_progress_hint (self);
879
880         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
881 }
882
883 static void
884 on_queue_changed (ModestMailOperationQueue *queue,
885                   ModestMailOperation *mail_op,
886                   ModestMailOperationQueueNotification type,
887                   ModestHeaderWindow *self)
888 {
889         ModestHeaderWindowPrivate *priv;
890
891         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
892
893         /* If this operations was created by another window, do nothing */
894         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
895                 return;
896
897         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
898                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
899                                                                G_OBJECT (mail_op),
900                                                                "operation-started",
901                                                                G_CALLBACK (on_mail_operation_started),
902                                                                self);
903                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
904                                                                G_OBJECT (mail_op),
905                                                                "operation-finished",
906                                                                G_CALLBACK (on_mail_operation_finished),
907                                                                self);
908         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
909                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
910                                                                   G_OBJECT (mail_op),
911                                                                   "operation-started");
912                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
913                                                                   G_OBJECT (mail_op),
914                                                                   "operation-finished");
915         }
916 }
917
918 static void
919 modest_header_window_pack_toolbar (ModestHildon2Window *self,
920                                    GtkPackType pack_type,
921                                    GtkWidget *toolbar)
922 {
923         ModestHeaderWindowPrivate *priv;
924
925         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
926         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
927
928         if (pack_type == GTK_PACK_START) {
929                 gtk_box_pack_start (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
930         } else {
931                 gtk_box_pack_end (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
932         }
933 }
934
935 static void 
936 edit_mode_changed (ModestHeaderWindow *header_window,
937                    gint edit_mode_id,
938                    gboolean enabled,
939                    ModestHeaderWindow *self)
940 {
941         ModestHeaderWindowPrivate *priv;
942         ModestHeaderViewFilter filter = MODEST_HEADER_VIEW_FILTER_NONE;
943
944         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
945         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
946
947         switch (edit_mode_id) {
948         case EDIT_MODE_COMMAND_MOVE:
949                 filter = MODEST_HEADER_VIEW_FILTER_MOVEABLE;
950                 break;
951         case EDIT_MODE_COMMAND_DELETE:
952                 filter = MODEST_HEADER_VIEW_FILTER_DELETABLE;
953                 break;
954         case MODEST_HILDON2_WINDOW_EDIT_MODE_NONE:
955                 filter = MODEST_HEADER_VIEW_FILTER_NONE;
956                 break;
957         }
958
959         if (enabled)
960                 modest_header_view_set_filter (MODEST_HEADER_VIEW (priv->header_view), 
961                                                filter);
962         else
963                 modest_header_view_unset_filter (MODEST_HEADER_VIEW (priv->header_view), 
964                                                  filter);
965 }
966
967 static void 
968 on_sort_column_changed (GtkTreeSortable *treesortable,
969                         gpointer         user_data)
970 {
971         update_sort_button (MODEST_HEADER_WINDOW (user_data));
972 }
973
974 static void
975 update_sort_button (ModestHeaderWindow *self)
976 {
977         ModestHeaderWindowPrivate *priv;
978         GtkTreeSortable *sortable;
979         gint current_sort_colid = -1;
980         GtkSortType current_sort_type;
981         const gchar *value = NULL;
982
983         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
984         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
985                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (
986                                                                       GTK_TREE_VIEW (priv->header_view)))));
987
988         if (!gtk_tree_sortable_get_sort_column_id (sortable,
989                                                    &current_sort_colid, &current_sort_type)) {
990                 value =  _("mcen_li_sort_sender_date_newest");
991         } else {
992                 switch (current_sort_colid) {
993                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
994                 {
995                         GList *cols = NULL;
996                         cols = modest_header_view_get_columns (MODEST_HEADER_VIEW (priv->header_view));
997                         if (cols != NULL) {
998                                 gpointer flags_sort_type_pointer;
999                                 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), 
1000                                                                              MODEST_HEADER_VIEW_FLAG_SORT);
1001                                 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
1002                                         value = _("mcen_li_sort_priority");
1003                                 else
1004                                         value = _("mcen_li_sort_attachment");
1005                                 g_list_free(cols);      
1006                         }
1007                 } 
1008                 break;
1009                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
1010                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
1011                         if (current_sort_type == GTK_SORT_ASCENDING)
1012                                 value = _("mcen_li_sort_sender_recipient_az");
1013                         else
1014                                 value = _("mcen_li_sort_sender_recipient_za");
1015                         break;
1016                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
1017                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
1018                         if (current_sort_type == GTK_SORT_ASCENDING)
1019                                 value = _("mcen_li_sort_date_oldest");
1020                         else
1021                                 value = _("mcen_li_sort_date_newest");
1022                         break;
1023                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
1024                         if (current_sort_type == GTK_SORT_ASCENDING)
1025                                 value = _("mcen_li_sort_subject_az");
1026                         else
1027                                 value = _("mcen_li_sort_subject_za");
1028                         break;
1029                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
1030                         if (current_sort_type == GTK_SORT_ASCENDING)
1031                                 value = _("mcen_li_sort_size_smallest");
1032                         else
1033                                 value = _("mcen_li_sort_size_largest");
1034                         break;
1035                 } 
1036         }
1037
1038         hildon_button_set_value (HILDON_BUTTON (priv->sort_button), value?value:"");
1039 }