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