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