* Manages the first refresh in the connection policy
[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         
440         self  = MODEST_HEADER_WINDOW(g_object_new(MODEST_TYPE_HEADER_WINDOW, NULL));
441         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
442
443         priv->folder = g_object_ref (folder);
444
445         priv->contents_view = hildon_pannable_area_new ();
446
447         /* We need to do this here to properly listen for mail
448            operations because create_header_view launches a mail
449            operation */
450         priv->queue_change_handler =
451                 g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
452                                   "queue-changed",
453                                   G_CALLBACK (on_queue_changed),
454                                   self);
455
456         priv->header_view  = create_header_view (MODEST_WINDOW (self), folder);
457         priv->empty_view = create_empty_view (MODEST_WINDOW (self));
458
459         /* Transform the floating reference in a "hard" reference. We
460            need to do this because the widgets could be added/removed
461            to containers many times so we always need to keep a
462            reference. It could happen also that some widget is never
463            added to any container */
464         g_object_ref_sink (priv->header_view);
465         g_object_ref_sink (priv->empty_view);
466
467         g_signal_connect (G_OBJECT (self), "edit-mode-changed",
468                           G_CALLBACK (edit_mode_changed), (gpointer) self);
469
470         action_area_box = hildon_tree_view_get_action_area_box (GTK_TREE_VIEW (priv->header_view));
471         priv->new_message_button = hildon_button_new (0, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
472
473         hildon_button_set_title (HILDON_BUTTON (priv->new_message_button), _("mcen_ti_new_message"));
474         new_message_pixbuf = modest_platform_get_icon ("general_add", MODEST_ICON_SIZE_BIG);
475         hildon_button_set_image (HILDON_BUTTON (priv->new_message_button), gtk_image_new_from_pixbuf (new_message_pixbuf));
476         g_object_unref (new_message_pixbuf);
477
478         gtk_box_pack_start (GTK_BOX (action_area_box), priv->new_message_button, TRUE, TRUE, 0);
479         gtk_widget_show_all (priv->new_message_button);
480         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), TRUE);
481         
482         setup_menu (self);
483
484         priv->top_vbox = gtk_vbox_new (FALSE, 0);
485         gtk_box_pack_end (GTK_BOX (priv->top_vbox), priv->contents_view, TRUE, TRUE, 0);
486
487         gtk_container_add (GTK_CONTAINER (self), priv->top_vbox);
488
489         gtk_widget_show (priv->contents_view);
490         gtk_widget_show (priv->top_vbox);
491
492         connect_signals (MODEST_HEADER_WINDOW (self));
493
494         update_view (self, NULL);
495
496         /* Load previous osso state, for instance if we are being restored from 
497          * hibernation:  */
498         modest_osso_load_state ();
499
500         /* Get device name */
501         modest_maemo_utils_get_device_name ();
502
503         app = hildon_program_get_instance ();
504         hildon_program_add_window (app, HILDON_WINDOW (self));
505
506         /* Set window icon */
507         window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG);
508         if (window_icon) {
509                 gtk_window_set_icon (GTK_WINDOW (self), window_icon);
510                 g_object_unref (window_icon);
511         }
512
513         /* Set window title */
514         if (TNY_IS_FOLDER (folder)) {
515                 gchar *folder_name;
516
517                 folder_name = modest_tny_folder_get_display_name (folder);
518                 gtk_window_set_title (GTK_WINDOW (self), folder_name);
519                 g_free (folder_name);
520         }
521
522         /* Dont't restore settings here, 
523          * because it requires a gtk_widget_show(), 
524          * and we don't want to do that until later,
525          * so that the UI is not visible for non-menu D-Bus activation.
526          */
527
528         /* setup edit modes */
529         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE,
530                                                   _("mcen_ti_edit_delete"), _HL("wdgt_bd_delete"),
531                                                   GTK_TREE_VIEW (priv->header_view),
532                                                   GTK_SELECTION_MULTIPLE,
533                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_delete_message));
534         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE,
535                                                   _("mcen_ti_edit_move"), _HL("wdgt_bd_move"),
536                                                   GTK_TREE_VIEW (priv->header_view),
537                                                   GTK_SELECTION_MULTIPLE,
538                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_move_to));
539
540
541         modest_window_set_active_account (MODEST_WINDOW (self), account_name);
542         mgr = modest_runtime_get_account_mgr ();
543         settings = modest_account_mgr_load_account_settings (mgr, account_name);
544         if (settings) {
545                 store_settings = modest_account_settings_get_store_settings (settings);
546                 if (store_settings) {
547                         priv->current_store_account = 
548                                 g_strdup (modest_server_account_settings_get_account_name (store_settings));
549                         g_object_unref (store_settings);
550                 }
551                 g_object_unref (settings);
552         }
553
554         update_progress_hint (self);
555         update_sort_button (self);
556
557         return MODEST_WINDOW(self);
558 }
559
560 ModestHeaderView *
561 modest_header_window_get_header_view (ModestHeaderWindow *self)
562 {
563         ModestHeaderWindowPrivate *priv = NULL;
564
565         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
566
567         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
568         
569         return MODEST_HEADER_VIEW (priv->header_view);
570 }
571
572 static void setup_menu (ModestHeaderWindow *self)
573 {
574         ModestHeaderWindowPrivate *priv;
575
576         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
577         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
578
579         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
580                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
581                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
582         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_messages"), NULL,
583                                            APP_MENU_CALLBACK (set_moveto_edit_mode),
584                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
585         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_delete_messages"), NULL,
586                                            APP_MENU_CALLBACK (set_delete_edit_mode),
587                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_delete));
588         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_folder_details"), NULL,
589                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
590                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
591         priv->sort_button = hildon_button_new (MODEST_EDITABLE_SIZE,
592                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL);
593         hildon_button_set_title (HILDON_BUTTON (priv->sort_button), _("mcen_me_sort"));
594         g_signal_connect (G_OBJECT (priv->sort_button), "clicked",
595                           G_CALLBACK (modest_ui_actions_on_sort), (gpointer) self);
596         hildon_button_set_style(HILDON_BUTTON (priv->sort_button), HILDON_BUTTON_STYLE_PICKER);
597         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->sort_button),
598                                                   modest_ui_dimming_rules_on_sort);
599         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_sendandreceive"), NULL,
600                                            APP_MENU_CALLBACK (modest_ui_actions_on_send_receive),
601                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_send_receive));
602         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_outbox_cancelsend"), NULL,
603                                            APP_MENU_CALLBACK (modest_ui_actions_cancel_send),
604                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_cancel_sending_all));
605 }
606
607 static void 
608 update_view (ModestHeaderWindow *self,
609              TnyFolderChange *change)
610 {
611         ModestHeaderWindowPrivate *priv = NULL;
612         gboolean refilter = FALSE;
613         gboolean folder_empty = FALSE;
614         gboolean all_marked_as_deleted = FALSE;
615
616         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
617         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
618         g_return_if_fail (priv->folder);
619
620         if (change != NULL) {
621                 TnyFolderChangeChanged changed;
622
623                 changed = tny_folder_change_get_changed (change);
624                 /* If something changes */
625                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
626                         folder_empty = (((guint) tny_folder_change_get_new_all_count (change)) == 0);
627                 else
628                         folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
629
630                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
631                         refilter = TRUE;
632         } else {
633                 folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (priv->folder))) == 0);
634         }
635
636         /* Check if all messages are marked to be deleted */
637         all_marked_as_deleted = modest_header_view_is_empty (MODEST_HEADER_VIEW (priv->header_view));
638         folder_empty = folder_empty || all_marked_as_deleted;
639
640         /* Set style of headers view */
641         set_contents_state (self, folder_empty?CONTENTS_STATE_EMPTY:CONTENTS_STATE_HEADERS);
642
643         if (refilter)
644                 modest_header_view_refilter (MODEST_HEADER_VIEW (priv->header_view));
645 }
646
647 static void 
648 set_contents_state (ModestHeaderWindow *self, 
649                     ContentsState state)
650 {
651         ModestHeaderWindowPrivate *priv = NULL;
652
653         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
654         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
655
656         if (priv->contents_state == state)
657                 return;
658
659         /* Remove from container the old content */
660         switch (priv->contents_state) {
661         case CONTENTS_STATE_EMPTY:
662                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->empty_view);
663                 break;
664         case CONTENTS_STATE_HEADERS:
665                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->header_view);
666                 break;
667         case CONTENTS_STATE_NONE:
668                 break;
669         }
670
671         /* Add the new content */
672         switch (state) {
673         case CONTENTS_STATE_EMPTY:
674                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->empty_view);
675                 gtk_widget_show (priv->empty_view);
676                 break;
677         case CONTENTS_STATE_HEADERS:
678                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->header_view);
679                 gtk_widget_show (priv->header_view);
680                 break;
681         case CONTENTS_STATE_NONE:
682                 break;
683         }
684         priv->contents_state = state;
685 }
686
687 static void
688 on_msg_count_changed (ModestHeaderView *header_view,
689                       TnyFolder *folder,
690                       TnyFolderChange *change,
691                       ModestHeaderWindow *header_window)
692 {
693         g_return_if_fail (MODEST_IS_HEADER_WINDOW (header_window));
694
695         update_view (MODEST_HEADER_WINDOW (header_window), change);
696 }
697
698 static void 
699 on_header_activated (ModestHeaderView *header_view,
700                      TnyHeader *header,
701                      GtkTreePath *path,
702                      ModestHeaderWindow *header_window)
703 {
704         modest_ui_actions_on_header_activated (header_view, header, path, MODEST_WINDOW (header_window));
705 }
706
707 static void
708 updating_banner_destroyed (gpointer data,
709                            GObject *where_the_object_was)
710 {
711         ModestHeaderWindowPrivate *priv = NULL;
712
713         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
714
715         priv->updating_banner = NULL;
716 }
717
718 static gboolean
719 show_updating_banner (gpointer user_data)
720 {
721         ModestHeaderWindowPrivate *priv = NULL;
722
723         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
724
725         if (priv->updating_banner == NULL) {
726
727                 /* We're outside the main lock */
728                 gdk_threads_enter ();
729                 priv->updating_banner = 
730                         modest_platform_animation_banner (GTK_WIDGET (user_data), NULL,
731                                                           _CS ("ckdg_pb_updating"));
732
733                 /* We need this because banners in Maemo could be
734                    destroyed by dialogs so we need to properly update
735                    our reference to it */
736                 g_object_weak_ref (G_OBJECT (priv->updating_banner),
737                                    updating_banner_destroyed,
738                                    user_data);
739                 gdk_threads_leave ();
740         }
741
742         /* Remove timeout */
743         priv->updating_banner_timeout = 0;
744         return FALSE;
745 }
746
747 /**
748  * We use this function to show/hide a progress banner showing
749  * "Updating" while the header view is being filled. We're not showing
750  * it unless the update takes more than 2 seconds
751  *
752  * If starting = TRUE then the refresh is starting, otherwise it means
753  * that is has just finished
754  */
755 static void 
756 on_updating_msg_list (ModestHeaderView *header_view,
757                       gboolean starting,
758                       gpointer user_data)
759 {
760         ModestHeaderWindowPrivate *priv = NULL;
761
762         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
763         
764         /* Remove old timeout */
765         if (priv->updating_banner_timeout > 0) {
766                 g_source_remove (priv->updating_banner_timeout);
767                 priv->updating_banner_timeout = 0;
768         }
769
770         /* Create a new timeout */
771         if (starting) {
772                 priv->updating_banner_timeout = 
773                         g_timeout_add (2000, show_updating_banner, user_data);
774         } else {
775                 /* Remove the banner if exists */
776                 if (priv->updating_banner) {
777                         gtk_widget_destroy (priv->updating_banner);
778                         priv->updating_banner = NULL;
779                 }
780         }
781 }
782
783 static void
784 set_delete_edit_mode (GtkButton *button,
785                       ModestHeaderWindow *self)
786 {
787         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE);
788 }
789
790 static void
791 set_moveto_edit_mode (GtkButton *button,
792                     ModestHeaderWindow *self)
793 {
794         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE);
795 }
796
797 static gboolean 
798 on_expose_event(GtkTreeView *header_view,
799                 GdkEventExpose *event,
800                 gpointer user_data)
801 {
802         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
803         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
804
805         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
806
807         if (priv->autoscroll)
808                 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (priv->contents_view), 0.0, 0.0);
809
810         return FALSE;
811 }
812
813 static gboolean 
814 on_map_event(GtkWidget *widget,
815              GdkEvent *event,
816              gpointer user_data)
817 {
818         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
819         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
820
821         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
822
823         if (priv->progress_hint) {
824                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
825         }
826         return FALSE;
827 }
828
829 static void
830 on_progress_list_changed (ModestWindowMgr *mgr,
831                           ModestHeaderWindow *self)
832 {
833         update_progress_hint (self);
834 }
835
836 static gboolean
837 has_active_operations (ModestHeaderWindow *self)
838 {
839         GSList *operations = NULL, *node;
840         ModestMailOperationQueue *queue;
841         gboolean has_active = FALSE;
842
843         queue = modest_runtime_get_mail_operation_queue ();
844         operations = modest_mail_operation_queue_get_by_source (queue, G_OBJECT (self));
845
846         for (node = operations; node != NULL; node = g_slist_next (node)) {
847                 if (!modest_mail_operation_is_finished (MODEST_MAIL_OPERATION (node->data))) {
848                         has_active = TRUE;
849                         break;
850                 }
851         }
852
853         if (operations) {
854                 g_slist_foreach (operations, (GFunc) g_object_unref, NULL);
855                 g_slist_free (operations);
856         }
857
858         return has_active;
859 }
860
861 static void
862 update_progress_hint (ModestHeaderWindow *self)
863 {
864         ModestHeaderWindowPrivate *priv;
865
866         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
867
868         priv->progress_hint = FALSE;
869
870         if (has_active_operations (self)) {
871                 priv->progress_hint = TRUE;
872         }
873
874         if (!priv->progress_hint && priv->current_store_account) {
875                 priv->progress_hint = 
876                         modest_window_mgr_has_progress_operation_on_account (modest_runtime_get_window_mgr (),
877                                                                              priv->current_store_account);
878         }
879
880         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
881
882         if (GTK_WIDGET_VISIBLE (self)) {
883                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), priv->progress_hint?1:0);
884         }
885 }
886
887 gboolean
888 modest_header_window_toolbar_on_transfer_mode     (ModestHeaderWindow *self)
889 {
890         ModestHeaderWindowPrivate *priv= NULL; 
891
892         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
893         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
894
895         return priv->progress_hint;
896 }
897
898 gboolean 
899 modest_header_window_transfer_mode_enabled (ModestHeaderWindow *self)
900 {
901         ModestHeaderWindowPrivate *priv;
902         
903         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);   
904         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
905
906         return priv->progress_hint;
907 }
908
909 static void 
910 on_mail_operation_started (ModestMailOperation *mail_op,
911                            gpointer user_data)
912 {
913         ModestHeaderWindow *self;
914         ModestMailOperationTypeOperation op_type;
915         GObject *source = NULL;
916
917         self = MODEST_HEADER_WINDOW (user_data);
918         op_type = modest_mail_operation_get_type_operation (mail_op);
919         source = modest_mail_operation_get_source(mail_op);
920         if (G_OBJECT (self) == source) {
921                 update_progress_hint (self);
922         }
923         g_object_unref (source);
924 }
925
926 static void 
927 on_mail_operation_finished (ModestMailOperation *mail_op,
928                             gpointer user_data)
929 {
930         ModestHeaderWindow *self;
931
932         self = MODEST_HEADER_WINDOW (user_data);
933
934         /* Don't disable the progress hint if there are more pending
935            operations from this window */
936         update_progress_hint (self);
937
938         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
939 }
940
941 static void
942 on_queue_changed (ModestMailOperationQueue *queue,
943                   ModestMailOperation *mail_op,
944                   ModestMailOperationQueueNotification type,
945                   ModestHeaderWindow *self)
946 {
947         ModestHeaderWindowPrivate *priv;
948
949         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
950
951         /* If this operations was created by another window, do nothing */
952         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
953                 return;
954
955         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
956                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
957                                                                G_OBJECT (mail_op),
958                                                                "operation-started",
959                                                                G_CALLBACK (on_mail_operation_started),
960                                                                self);
961                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
962                                                                G_OBJECT (mail_op),
963                                                                "operation-finished",
964                                                                G_CALLBACK (on_mail_operation_finished),
965                                                                self);
966         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
967                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
968                                                                   G_OBJECT (mail_op),
969                                                                   "operation-started");
970                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
971                                                                   G_OBJECT (mail_op),
972                                                                   "operation-finished");
973         }
974 }
975
976 static void
977 modest_header_window_pack_toolbar (ModestHildon2Window *self,
978                                    GtkPackType pack_type,
979                                    GtkWidget *toolbar)
980 {
981         ModestHeaderWindowPrivate *priv;
982
983         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
984         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
985
986         if (pack_type == GTK_PACK_START) {
987                 gtk_box_pack_start (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
988         } else {
989                 gtk_box_pack_end (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
990         }
991 }
992
993 static void 
994 edit_mode_changed (ModestHeaderWindow *header_window,
995                    gint edit_mode_id,
996                    gboolean enabled,
997                    ModestHeaderWindow *self)
998 {
999         ModestHeaderWindowPrivate *priv;
1000         ModestHeaderViewFilter filter = MODEST_HEADER_VIEW_FILTER_NONE;
1001
1002         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
1003         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1004
1005         switch (edit_mode_id) {
1006         case EDIT_MODE_COMMAND_MOVE:
1007                 filter = MODEST_HEADER_VIEW_FILTER_MOVEABLE;
1008                 break;
1009         case EDIT_MODE_COMMAND_DELETE:
1010                 filter = MODEST_HEADER_VIEW_FILTER_DELETABLE;
1011                 break;
1012         case MODEST_HILDON2_WINDOW_EDIT_MODE_NONE:
1013                 filter = MODEST_HEADER_VIEW_FILTER_NONE;
1014                 break;
1015         }
1016
1017         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), !enabled);
1018         if (enabled) {
1019                 modest_header_view_set_filter (MODEST_HEADER_VIEW (priv->header_view), 
1020                                                filter);
1021         } else {
1022                 GtkTreeSelection *sel;
1023
1024                 /* Unselect all. This will prevent us from keeping a
1025                    reference to a TnyObject that we don't want to
1026                    have */
1027                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->header_view));
1028                 gtk_tree_selection_unselect_all (sel);
1029
1030                 modest_header_view_unset_filter (MODEST_HEADER_VIEW (priv->header_view), 
1031                                                  filter);
1032         }
1033 }
1034
1035 static void 
1036 on_sort_column_changed (GtkTreeSortable *treesortable,
1037                         gpointer         user_data)
1038 {
1039         update_sort_button (MODEST_HEADER_WINDOW (user_data));
1040 }
1041
1042 static void
1043 update_sort_button (ModestHeaderWindow *self)
1044 {
1045         ModestHeaderWindowPrivate *priv;
1046         GtkTreeSortable *sortable;
1047         gint current_sort_colid = -1;
1048         GtkSortType current_sort_type;
1049         const gchar *value = NULL;
1050
1051         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1052         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
1053                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (
1054                                                                       GTK_TREE_VIEW (priv->header_view)))));
1055
1056         if (!gtk_tree_sortable_get_sort_column_id (sortable,
1057                                                    &current_sort_colid, &current_sort_type)) {
1058                 value =  _("mcen_li_sort_sender_date_newest");
1059         } else {
1060                 switch (current_sort_colid) {
1061                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
1062                 {
1063                         GList *cols = NULL;
1064                         cols = modest_header_view_get_columns (MODEST_HEADER_VIEW (priv->header_view));
1065                         if (cols != NULL) {
1066                                 gpointer flags_sort_type_pointer;
1067                                 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), 
1068                                                                              MODEST_HEADER_VIEW_FLAG_SORT);
1069                                 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
1070                                         value = _("mcen_li_sort_priority");
1071                                 else
1072                                         value = _("mcen_li_sort_attachment");
1073                                 g_list_free(cols);      
1074                         }
1075                 } 
1076                 break;
1077                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
1078                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
1079                         if (current_sort_type == GTK_SORT_ASCENDING)
1080                                 value = _("mcen_li_sort_sender_recipient_az");
1081                         else
1082                                 value = _("mcen_li_sort_sender_recipient_za");
1083                         break;
1084                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
1085                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
1086                         if (current_sort_type == GTK_SORT_ASCENDING)
1087                                 value = _("mcen_li_sort_date_oldest");
1088                         else
1089                                 value = _("mcen_li_sort_date_newest");
1090                         break;
1091                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
1092                         if (current_sort_type == GTK_SORT_ASCENDING)
1093                                 value = _("mcen_li_sort_subject_az");
1094                         else
1095                                 value = _("mcen_li_sort_subject_za");
1096                         break;
1097                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
1098                         if (current_sort_type == GTK_SORT_ASCENDING)
1099                                 value = _("mcen_li_sort_size_smallest");
1100                         else
1101                                 value = _("mcen_li_sort_size_largest");
1102                         break;
1103                 } 
1104         }
1105
1106         hildon_button_set_value (HILDON_BUTTON (priv->sort_button), value?value:"");
1107 }