If handle_calendar is not handler, then don't show the calendar containers
[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 <hildon/hildon-pannable-area.h>
32 #include <hildon/hildon-helper.h>
33 #include <modest-window-mgr.h>
34 #include <modest-window-priv.h>
35 #include <modest-signal-mgr.h>
36 #include <modest-runtime.h>
37 #include <modest-platform.h>
38 #include <modest-maemo-utils.h>
39 #include <modest-icon-names.h>
40 #include <modest-ui-constants.h>
41 #include <modest-account-mgr.h>
42 #include <modest-account-mgr-helpers.h>
43 #include <modest-defs.h>
44 #include <modest-widget-memory.h>
45 #include <modest-ui-actions.h>
46 #include <modest-platform.h>
47 #include <modest-text-utils.h>
48 #include <hildon/hildon-button.h>
49 #include <hildon/hildon-program.h>
50 #include <hildon/hildon-banner.h>
51 #include <modest-ui-dimming-rules.h>
52 #include <modest-tny-folder.h>
53 #include <modest-tny-account.h>
54 #include <tny-simple-list.h>
55
56 #define SHOW_LATEST_SIZE 250
57
58 typedef enum {
59         CONTENTS_STATE_NONE = 0,
60         CONTENTS_STATE_EMPTY = 1,
61         CONTENTS_STATE_HEADERS = 2
62 } ContentsState;
63
64 typedef enum {
65         EDIT_MODE_COMMAND_MOVE = 1,
66         EDIT_MODE_COMMAND_DELETE = 2,
67 } EditModeCommand;
68
69 typedef struct _ModestHeaderWindowPrivate ModestHeaderWindowPrivate;
70 struct _ModestHeaderWindowPrivate {
71
72         GtkWidget *header_view;
73         GtkWidget *empty_view;
74         GtkWidget *contents_view;
75         GtkWidget *top_vbox;
76         GtkWidget *new_message_button;
77         GtkWidget *show_more_button;
78
79         /* state bar */
80         ContentsState contents_state;
81
82         /* autoscroll */
83         gboolean autoscroll;
84
85         /* banners */
86         GtkWidget *updating_banner;
87         guint updating_banner_timeout;
88
89         /* signals */
90         GSList *sighandlers;
91         gulong queue_change_handler;
92         gulong sort_column_handler;
93         gulong notify_model;
94
95         /* progress hint */
96         gboolean progress_hint;
97         gchar *current_store_account;
98
99         /* sort button */
100         GtkWidget *sort_button;
101
102         /* CSM menu */
103         GtkWidget *csm_menu;
104         gdouble x_coord;
105         gdouble y_coord;
106
107         /* weak refs */
108         GtkTreeModel *model_weak_ref;
109 };
110 #define MODEST_HEADER_WINDOW_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE((o), \
111                                                                           MODEST_TYPE_HEADER_WINDOW, \
112                                                                           ModestHeaderWindowPrivate))
113
114 /* 'private'/'protected' functions */
115 static void modest_header_window_class_init  (ModestHeaderWindowClass *klass);
116 static void modest_header_window_init        (ModestHeaderWindow *obj);
117 static void modest_header_window_finalize    (GObject *obj);
118 static void modest_header_window_dispose     (GObject *obj);
119
120 static void connect_signals (ModestHeaderWindow *self);
121 static void modest_header_window_disconnect_signals (ModestWindow *self);
122
123 static void setup_menu (ModestHeaderWindow *self);
124 static GtkWidget *create_empty_view (ModestWindow *self);
125 static GtkWidget *create_header_view (ModestWindow *progress_window,
126                                       TnyFolder *folder);
127
128 static void update_view (ModestHeaderWindow *self,
129                          TnyFolderChange *change);
130 static void set_contents_state (ModestHeaderWindow *window,
131                                 ContentsState state);
132
133 static void on_msg_count_changed (ModestHeaderView *header_view,
134                                   TnyFolder *folder,
135                                   TnyFolderChange *change,
136                                   ModestHeaderWindow *header_window);
137 static void on_header_activated (ModestHeaderView *header_view,
138                                  TnyHeader *header,
139                                  GtkTreePath *path,
140                                  ModestHeaderWindow *header_window);
141 static void on_updating_msg_list (ModestHeaderView *header_view,
142                                   gboolean starting,
143                                   gpointer user_data);
144 static void set_delete_edit_mode (GtkButton *button,
145                                   ModestHeaderWindow *self);
146 static void set_moveto_edit_mode (GtkButton *button,
147                                   ModestHeaderWindow *self);
148 static gboolean on_expose_event(GtkTreeView *header_view,
149                                 GdkEventExpose *event,
150                                 gpointer user_data);
151 static gboolean on_map_event (GtkWidget *widget,
152                               GdkEvent *event,
153                               gpointer userdata);
154 static void on_vertical_movement (HildonPannableArea *area,
155                                   HildonMovementDirection direction,
156                                   gdouble x, gdouble y, gpointer user_data);
157 static void on_queue_changed    (ModestMailOperationQueue *queue,
158                                  ModestMailOperation *mail_op,
159                                  ModestMailOperationQueueNotification type,
160                                  ModestHeaderWindow *self);
161 static void modest_header_window_pack_toolbar (ModestHildon2Window *self,
162                                                GtkPackType pack_type,
163                                                GtkWidget *toolbar);
164 static void edit_mode_changed (ModestHeaderWindow *header_window,
165                                gint edit_mode_id,
166                                gboolean enabled,
167                                ModestHeaderWindow *self);
168 static void on_progress_list_changed (ModestWindowMgr *mgr,
169                                       ModestHeaderWindow *self);
170 static void update_progress_hint (ModestHeaderWindow *self);
171 static void on_sort_column_changed (GtkTreeSortable *treesortable,
172                                     gpointer         user_data);
173 static void update_sort_button (ModestHeaderWindow *self);
174 static void on_horizontal_movement (HildonPannableArea *hildonpannable,
175                                     gint                direction,
176                                     gdouble             initial_x,
177                                     gdouble             initial_y,
178                                     gpointer            user_data);
179 static void on_header_view_model_destroyed (gpointer user_data,
180                                             GObject *model);
181 static gboolean on_key_press(GtkWidget *widget,
182                                         GdkEventKey *event,
183                                         gpointer user_data);
184 static void modest_header_window_show_more (GtkAction *action, ModestWindow *win);
185
186 /* globals */
187 static GtkWindowClass *parent_class = NULL;
188
189 #define EMPTYVIEW_XALIGN 0.5
190 #define EMPTYVIEW_YALIGN 0.5
191 #define EMPTYVIEW_XSPACE 1.0
192 #define EMPTYVIEW_YSPACE 1.0
193
194
195
196 /************************************************************************/
197
198 GType
199 modest_header_window_get_type (void)
200 {
201         static GType my_type = 0;
202         if (!my_type) {
203                 static const GTypeInfo my_info = {
204                         sizeof(ModestHeaderWindowClass),
205                         NULL,           /* base init */
206                         NULL,           /* base finalize */
207                         (GClassInitFunc) modest_header_window_class_init,
208                         NULL,           /* class finalize */
209                         NULL,           /* class data */
210                         sizeof(ModestHeaderWindow),
211                         1,              /* n_preallocs */
212                         (GInstanceInitFunc) modest_header_window_init,
213                         NULL
214                 };
215                 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
216                                                   "ModestHeaderWindow",
217                                                   &my_info, 0);
218         }
219         return my_type;
220 }
221
222 static void
223 modest_header_window_class_init (ModestHeaderWindowClass *klass)
224 {
225         GObjectClass *gobject_class;
226         gobject_class = (GObjectClass*) klass;
227         ModestWindowClass *modest_window_class = (ModestWindowClass *) klass;
228         ModestHildon2WindowClass *modest_hildon2_window_class = (ModestHildon2WindowClass *) klass;
229
230         parent_class            = g_type_class_peek_parent (klass);
231         gobject_class->finalize = modest_header_window_finalize;
232         gobject_class->dispose = modest_header_window_dispose;
233
234         g_type_class_add_private (gobject_class, sizeof(ModestHeaderWindowPrivate));
235         
236         modest_window_class->disconnect_signals_func = modest_header_window_disconnect_signals;
237         modest_hildon2_window_class->pack_toolbar_func = modest_header_window_pack_toolbar;
238 }
239
240 static void
241 modest_header_window_init (ModestHeaderWindow *obj)
242 {
243         ModestHeaderWindowPrivate *priv;
244
245         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
246
247         priv->sighandlers = NULL;
248
249         priv->header_view = NULL;
250         priv->empty_view = NULL;
251         priv->top_vbox = NULL;
252         priv->contents_view = NULL;
253         priv->contents_state = CONTENTS_STATE_NONE;
254         priv->updating_banner = NULL;
255         priv->updating_banner_timeout = 0;
256         priv->autoscroll = TRUE;
257         priv->progress_hint = FALSE;
258         priv->queue_change_handler = 0;
259         priv->sort_column_handler = 0;
260         priv->model_weak_ref = NULL;
261         priv->current_store_account = NULL;
262         priv->sort_button = NULL;
263         priv->new_message_button = NULL;
264         priv->show_more_button = NULL;
265         priv->x_coord = 0;
266         priv->y_coord = 0;
267         priv->notify_model = 0;
268
269         modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
270                                             GTK_WINDOW(obj),
271                                             "applications_email_headerview");
272 }
273
274 static void
275 modest_header_window_dispose (GObject *obj)
276 {
277         ModestHeaderWindowPrivate *priv;
278         TnyFolder *folder;
279
280         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
281
282         folder = modest_header_view_get_folder ((ModestHeaderView *) priv->header_view);
283         if (folder) {
284                 tny_folder_sync_async (folder, TRUE, NULL, NULL, NULL);
285                 g_object_unref (folder);
286         }
287
288         G_OBJECT_CLASS(parent_class)->dispose (obj);
289 }
290
291 static void
292 modest_header_window_finalize (GObject *obj)
293 {
294         ModestHeaderWindowPrivate *priv;
295
296         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(obj);
297
298         if (priv->model_weak_ref) {
299                 g_object_weak_unref ((GObject *) priv->model_weak_ref,
300                                      on_header_view_model_destroyed,
301                                      obj);
302                 if (g_signal_handler_is_connected (G_OBJECT (priv->model_weak_ref),
303                                                    priv->sort_column_handler)) {
304                         g_signal_handler_disconnect (G_OBJECT (priv->model_weak_ref),
305                                                      priv->sort_column_handler);
306                 }
307                 on_header_view_model_destroyed (obj, (GObject *) priv->model_weak_ref);
308         }
309
310         modest_header_window_disconnect_signals (MODEST_WINDOW (obj));
311
312         g_object_unref (priv->header_view);
313         g_object_unref (priv->empty_view);
314
315         if (priv->current_store_account) {
316                 g_free (priv->current_store_account);
317                 priv->current_store_account = NULL;
318         }
319
320         if (priv->updating_banner_timeout > 0) {
321                 g_source_remove (priv->updating_banner_timeout);
322                 priv->updating_banner_timeout = 0;
323         }
324         if (priv->updating_banner) {
325                 gtk_widget_destroy (priv->updating_banner);
326                 priv->updating_banner = NULL;
327         }
328
329         G_OBJECT_CLASS(parent_class)->finalize (obj);
330 }
331
332 static void
333 modest_header_window_disconnect_signals (ModestWindow *self)
334 {
335         ModestHeaderWindowPrivate *priv;
336
337         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
338
339         if (g_signal_handler_is_connected ((GObject*) priv->header_view, priv->notify_model)) {
340                 g_signal_handler_disconnect ((GObject*) priv->header_view, priv->notify_model);
341                 priv->notify_model = 0;
342         }
343
344         if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
345                                            priv->queue_change_handler)) {
346                 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), 
347                                              priv->queue_change_handler);
348                 priv->queue_change_handler = 0;
349         }
350
351         if (priv->header_view) {
352                 GtkTreeModel *sortable;
353
354                 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->header_view));
355                 if (sortable) {
356                         if (g_signal_handler_is_connected (G_OBJECT (sortable),
357                                                            priv->sort_column_handler)) {
358                                 g_signal_handler_disconnect (G_OBJECT (sortable),
359                                                              priv->sort_column_handler);
360                                 priv->sort_column_handler = 0;
361                         }
362                 }
363         }
364
365         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
366         priv->sighandlers = NULL;
367
368 }
369
370 static void
371 connect_signals (ModestHeaderWindow *self)
372 {
373         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
374
375         /* header view */
376
377         priv->sighandlers = 
378                 modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), 
379                                            "msg_count_changed",
380                                            G_CALLBACK(on_msg_count_changed), self);
381         priv->sighandlers =
382                 modest_signal_mgr_connect (priv->sighandlers, G_OBJECT (priv->header_view),
383                                            "header-activated",
384                                            G_CALLBACK (on_header_activated), self);
385         priv->sighandlers = 
386                 modest_signal_mgr_connect (priv->sighandlers,
387                                            G_OBJECT (priv->header_view), 
388                                            "updating-msg-list",
389                                            G_CALLBACK (on_updating_msg_list), 
390                                            self);
391         priv->sighandlers =
392                 modest_signal_mgr_connect (priv->sighandlers,
393                                            G_OBJECT (priv->header_view),
394                                            "expose-event",
395                                            G_CALLBACK (on_expose_event),
396                                            self);
397
398         priv->sighandlers =
399                 modest_signal_mgr_connect (priv->sighandlers,
400                                            G_OBJECT (self),
401                                            "map-event",
402                                            G_CALLBACK (on_map_event),
403                                            self);
404
405         priv->sighandlers =
406                 modest_signal_mgr_connect (priv->sighandlers,
407                                            G_OBJECT (priv->contents_view), 
408                                            "vertical-movement", 
409                                            G_CALLBACK (on_vertical_movement), 
410                                            self);
411
412         /* Mail Operation Queue */
413         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
414                                                        G_OBJECT (modest_runtime_get_window_mgr ()),
415                                                        "progress-list-changed",
416                                                        G_CALLBACK (on_progress_list_changed), self);
417         priv->sighandlers =
418                 modest_signal_mgr_connect (priv->sighandlers,
419                                            G_OBJECT (priv->new_message_button),
420                                            "clicked",
421                                            G_CALLBACK (modest_ui_actions_on_new_msg), self);
422
423         priv->sighandlers =
424                 modest_signal_mgr_connect (priv->sighandlers,
425                                            G_OBJECT (priv->show_more_button),
426                                            "clicked",
427                                            G_CALLBACK (modest_header_window_show_more), self);
428
429         /* Delete using horizontal gesture */
430         /* DISABLED because it's unreliabile */
431         if (FALSE) {
432                 priv->sighandlers =
433                         modest_signal_mgr_connect (priv->sighandlers,
434                                                    (GObject *) priv->contents_view,
435                                                    "horizontal-movement",
436                                                    G_CALLBACK (on_horizontal_movement),
437                                                    self);
438         }
439
440
441         g_signal_connect(G_OBJECT(self), "key-press-event",
442                         G_CALLBACK(on_key_press), self);
443 }
444
445 static void
446 folder_refreshed_cb (ModestMailOperation *mail_op,
447                      TnyFolder *folder,
448                      gpointer user_data)
449 {
450         /* Update the view (folder could be empty) */
451         update_view (MODEST_HEADER_WINDOW (user_data), NULL);
452 }
453
454 static gboolean
455 tap_and_hold_query_cb (GtkWidget *header_view,
456                        GdkEvent *event,
457                        gpointer user_data)
458 {
459         ModestHeaderWindow *self;
460         ModestHeaderWindowPrivate *priv;
461
462         self = (ModestHeaderWindow *) user_data;
463         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
464
465         if (event->type == GDK_BUTTON_PRESS) {
466                 TnyHeader *header;
467
468                 priv->x_coord = ((GdkEventButton*)event)->x;
469                 priv->y_coord = ((GdkEventButton*)event)->y;
470
471                 /* Enable/Disable mark as (un)read */
472                 header = modest_header_view_get_header_at_pos ((ModestHeaderView *) header_view,
473                                                                priv->x_coord, priv->y_coord);
474                 if (header) {
475                         GList *children;
476                         GtkWidget *mark_read_item, *mark_unread_item;
477
478                         /* Show "mark as read" or "mark as unread" */
479                         children = gtk_container_get_children (GTK_CONTAINER (priv->csm_menu));
480                         mark_read_item = (GtkWidget *) g_list_nth_data (children, 1);
481                         mark_unread_item = (GtkWidget *) g_list_nth_data (children, 2);
482
483                         if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN) {
484                                 gtk_widget_show (mark_unread_item);
485                                 gtk_widget_hide (mark_read_item);
486                         } else {
487                                 gtk_widget_show (mark_read_item);
488                                 gtk_widget_hide (mark_unread_item);
489                         }
490                         g_object_unref (header);
491                 } else {
492                         /* Do not show the CSM if there is no header below */
493                         return TRUE;
494                 }
495         }
496
497         return FALSE;
498 }
499
500 static void
501 delete_header (GtkWindow *parent,
502                TnyHeader *header)
503 {
504         gint response;
505         gchar *subject, *msg;
506
507         subject = tny_header_dup_subject (header);
508         if (!subject)
509                 subject = g_strdup (_("mail_va_no_subject"));
510
511         msg = g_strdup_printf (ngettext("emev_nc_delete_message", "emev_nc_delete_messages", 1),
512                                subject);
513         g_free (subject);
514
515         /* Confirmation dialog */
516         response = modest_platform_run_confirmation_dialog (parent, msg);
517         g_free (msg);
518
519         if (response == GTK_RESPONSE_OK) {
520                 ModestMailOperation *mail_op;
521                 TnyList *header_list;
522
523                 header_list = tny_simple_list_new ();
524                 tny_list_append (header_list, (GObject *) header);
525                 mail_op = modest_mail_operation_new ((GObject *) parent);
526                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
527                                                  mail_op);
528                 modest_mail_operation_remove_msgs (mail_op, header_list, FALSE);
529                 g_object_unref (mail_op);
530                 g_object_unref (header_list);
531         }
532 }
533
534
535 static void
536 on_delete_csm_activated (GtkMenuItem *item,
537                          gpointer user_data)
538 {
539         TnyHeader *header;
540         ModestHeaderWindow *self;
541         ModestHeaderWindowPrivate *priv;
542
543         self = (ModestHeaderWindow *) user_data;
544         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
545
546         header = modest_header_view_get_header_at_pos ((ModestHeaderView *) priv->header_view,
547                                                        priv->x_coord, priv->y_coord);
548         if (header) {
549                 delete_header ((GtkWindow *) self, header);
550                 g_object_unref (header);
551         }
552 }
553
554 static void
555 on_mark_read_csm_activated (GtkMenuItem *item,
556                             gpointer user_data)
557 {
558         TnyHeader *header;
559         ModestHeaderWindow *self;
560         ModestHeaderWindowPrivate *priv;
561
562         self = (ModestHeaderWindow *) user_data;
563         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
564
565         header = modest_header_view_get_header_at_pos ((ModestHeaderView *) priv->header_view,
566                                                        priv->x_coord, priv->y_coord);
567
568         if (header) {
569                 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
570                 g_object_unref (header);
571         }
572 }
573
574 static void
575 on_mark_unread_csm_activated (GtkMenuItem *item,
576                               gpointer user_data)
577 {
578         TnyHeader *header;
579         ModestHeaderWindow *self;
580         ModestHeaderWindowPrivate *priv;
581
582         self = (ModestHeaderWindow *) user_data;
583         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
584
585         header = modest_header_view_get_header_at_pos ((ModestHeaderView *) priv->header_view,
586                                                        priv->x_coord, priv->y_coord);
587
588         if (header) {
589                 tny_header_unset_flag (header, TNY_HEADER_FLAG_SEEN);
590                 g_object_unref (header);
591         }
592 }
593
594 static void
595 on_header_view_model_destroyed (gpointer user_data,
596                                 GObject *model)
597 {
598         ModestHeaderWindow *self;
599         ModestHeaderWindowPrivate *priv;
600
601         self = (ModestHeaderWindow *) user_data;
602         if (!GTK_IS_WIDGET (self))
603                 return;
604
605         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
606         priv->model_weak_ref = NULL;
607
608         priv->sort_column_handler = 0;
609 }
610
611 static void
612 on_header_view_model_changed (GObject *gobject,
613                               GParamSpec *arg1,
614                               gpointer user_data)
615 {
616         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
617         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
618         GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (gobject));
619
620         if (priv->model_weak_ref ) {
621                 g_object_weak_unref ((GObject *) priv->model_weak_ref,
622                                      on_header_view_model_destroyed,
623                                      self);
624                 if (g_signal_handler_is_connected (G_OBJECT (priv->model_weak_ref),
625                                                    priv->sort_column_handler)) {
626                         g_signal_handler_disconnect (G_OBJECT (priv->model_weak_ref),
627                                                      priv->sort_column_handler);
628                 }
629                 on_header_view_model_destroyed (self, (GObject *) priv->model_weak_ref);
630         }
631
632         if (!model)
633                 return;
634
635         /* Connect the signal. Listen to object destruction to disconnect it */
636         priv->sort_column_handler = g_signal_connect ((GObject *) model,
637                                                       "sort-column-changed",
638                                                       G_CALLBACK (on_sort_column_changed),
639                                                       self);
640         priv->model_weak_ref = model;
641         g_object_weak_ref ((GObject *) model, on_header_view_model_destroyed, self);
642 }
643
644 static GtkWidget *
645 create_header_view (ModestWindow *self, TnyFolder *folder)
646 {
647         GtkWidget *header_view;
648         GtkWidget *delete_item, *mark_read_item, *mark_unread_item;
649         ModestHeaderWindowPrivate *priv;
650         TnyAccount *account;
651         ModestProtocolType protocol_type;
652         gboolean limit_headers;
653
654         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
655
656         header_view  = modest_header_view_new (NULL, MODEST_HEADER_VIEW_STYLE_TWOLINES);
657
658         account = modest_tny_folder_get_account (folder);
659         limit_headers = FALSE;
660         if (account) {
661                 protocol_type = modest_tny_account_get_protocol_type (account);
662                 if (modest_protocol_registry_protocol_type_has_tag (modest_runtime_get_protocol_registry (),
663                                                                     protocol_type,
664                                                                     MODEST_PROTOCOL_REGISTRY_STORE_LIMIT_HEADER_WINDOW)) {
665                         limit_headers = TRUE;
666                 }
667                 g_object_unref (account);
668         }
669         modest_header_view_set_show_latest (MODEST_HEADER_VIEW (header_view), limit_headers?SHOW_LATEST_SIZE:0);
670
671         priv->notify_model = g_signal_connect ((GObject*) header_view, "notify::model",
672                                                G_CALLBACK (on_header_view_model_changed), self);
673
674         modest_header_view_set_folder (MODEST_HEADER_VIEW (header_view), folder,
675                                        TRUE, self, folder_refreshed_cb, self);
676         modest_header_view_set_filter (MODEST_HEADER_VIEW (header_view),
677                                        MODEST_HEADER_VIEW_FILTER_NONE);
678         modest_widget_memory_restore (modest_runtime_get_conf (), G_OBJECT(header_view),
679                                       MODEST_CONF_HEADER_VIEW_KEY);
680
681         /* Create CSM menu */
682         priv->csm_menu = gtk_menu_new ();
683         delete_item = gtk_menu_item_new_with_label (_HL("wdgt_bd_delete"));
684         mark_read_item = gtk_menu_item_new_with_label (_("mcen_me_inbox_mark_as_read"));
685         mark_unread_item = gtk_menu_item_new_with_label (_("mcen_me_inbox_mark_as_unread"));
686         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), delete_item);
687         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), mark_read_item);
688         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), mark_unread_item);
689         hildon_gtk_widget_set_theme_size (delete_item, MODEST_EDITABLE_SIZE);
690         hildon_gtk_widget_set_theme_size (mark_unread_item, MODEST_EDITABLE_SIZE);
691         hildon_gtk_widget_set_theme_size (mark_read_item, MODEST_EDITABLE_SIZE);
692         gtk_widget_show_all (priv->csm_menu);
693
694         /* Connect signals */
695         g_signal_connect ((GObject *) header_view, "tap-and-hold-query",
696                           G_CALLBACK (tap_and_hold_query_cb), self);
697         g_signal_connect ((GObject *) delete_item, "activate",
698                           G_CALLBACK (on_delete_csm_activated), self);
699         g_signal_connect ((GObject *) mark_read_item, "activate",
700                           G_CALLBACK (on_mark_read_csm_activated), self);
701         g_signal_connect ((GObject *) mark_unread_item, "activate",
702                           G_CALLBACK (on_mark_unread_csm_activated), self);
703
704         /* Add tap&hold handling */
705         gtk_widget_tap_and_hold_setup (header_view, priv->csm_menu, NULL, 0);
706
707         return header_view;
708 }
709
710 static GtkWidget *
711 create_empty_view (ModestWindow *self)
712 {
713         GtkWidget *viewport = NULL;
714         GtkWidget *label = NULL;
715         GtkWidget *align = NULL;
716         GtkWidget *vbox = NULL;
717         GtkWidget *hbox = NULL;
718         GtkWidget *button = NULL;
719         GdkPixbuf *new_message_pixbuf;
720         ModestHeaderWindowPrivate *priv;
721
722         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
723
724         vbox = gtk_vbox_new (0, FALSE);
725
726         align = gtk_alignment_new(EMPTYVIEW_XALIGN, EMPTYVIEW_YALIGN, EMPTYVIEW_XSPACE, EMPTYVIEW_YSPACE);
727         label = gtk_label_new (_("mcen_ia_nomessages"));
728         hildon_helper_set_logical_font (label, "LargeSystemFont");
729         gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
730         gtk_widget_show (label);
731         gtk_widget_show (align);
732         gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);  
733         gtk_container_add (GTK_CONTAINER (align), label);
734         gtk_box_pack_end (GTK_BOX (vbox), align, TRUE, TRUE, 0);
735
736         button = hildon_button_new (MODEST_EDITABLE_SIZE, 
737                                     HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
738
739         hildon_button_set_title (HILDON_BUTTON (button), _("mcen_ti_new_message"));
740         new_message_pixbuf = modest_platform_get_icon ("general_add", MODEST_ICON_SIZE_BIG);
741         hildon_button_set_image (HILDON_BUTTON (button), 
742                                  gtk_image_new_from_pixbuf (new_message_pixbuf));
743         g_object_unref (new_message_pixbuf);
744         gtk_widget_show_all (button);
745
746         hbox = gtk_hbox_new (TRUE, 0);
747         gtk_widget_show (hbox);
748         gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
749         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
750
751         gtk_widget_show (vbox);
752
753         g_signal_connect (button,
754                           "clicked",
755                           G_CALLBACK (modest_ui_actions_on_new_msg), self);
756
757         viewport = gtk_viewport_new ((GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 0, 0, 0), 
758                                      (GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
759         gtk_container_add (GTK_CONTAINER (viewport), vbox);
760
761         return viewport;
762 }
763
764 static void
765 on_vertical_movement (HildonPannableArea *area,
766                       HildonMovementDirection direction,
767                       gdouble x, gdouble y, gpointer user_data)
768 {
769         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
770         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
771
772         priv->autoscroll = FALSE;
773 }
774
775
776 ModestWindow *
777 modest_header_window_new (TnyFolder *folder, const gchar *account_name, const gchar *mailbox)
778 {
779         ModestHeaderWindow *self = NULL;
780         ModestHeaderWindowPrivate *priv = NULL;
781         HildonProgram *app;
782         GdkPixbuf *window_icon;
783         ModestAccountMgr *mgr;
784         ModestAccountSettings *settings = NULL;
785         ModestServerAccountSettings *store_settings = NULL;
786         GtkWidget *action_area_box;
787         GdkPixbuf *new_message_pixbuf;
788         GtkWidget *alignment;
789         gchar *account_display_name = NULL;
790
791         self  = MODEST_HEADER_WINDOW(g_object_new(MODEST_TYPE_HEADER_WINDOW, NULL));
792         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
793
794         priv->contents_view = hildon_pannable_area_new ();
795         alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
796         gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
797                                    HILDON_MARGIN_HALF, 0,
798                                    HILDON_MARGIN_DOUBLE, HILDON_MARGIN_DOUBLE);
799
800         /* We need to do this here to properly listen for mail
801            operations because create_header_view launches a mail
802            operation */
803         priv->queue_change_handler =
804                 g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
805                                   "queue-changed",
806                                   G_CALLBACK (on_queue_changed),
807                                   self);
808
809         priv->header_view  = create_header_view (MODEST_WINDOW (self), folder);
810         priv->empty_view = create_empty_view (MODEST_WINDOW (self));
811
812         /* Transform the floating reference in a "hard" reference. We
813            need to do this because the widgets could be added/removed
814            to containers many times so we always need to keep a
815            reference. It could happen also that some widget is never
816            added to any container */
817         g_object_ref_sink (priv->header_view);
818         g_object_ref_sink (priv->empty_view);
819
820         g_signal_connect (G_OBJECT (self), "edit-mode-changed",
821                           G_CALLBACK (edit_mode_changed), (gpointer) self);
822
823         action_area_box = hildon_tree_view_get_action_area_box (GTK_TREE_VIEW (priv->header_view));
824         priv->new_message_button = hildon_button_new (MODEST_EDITABLE_SIZE, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
825
826         hildon_button_set_title (HILDON_BUTTON (priv->new_message_button), _("mcen_ti_new_message"));
827         new_message_pixbuf = modest_platform_get_icon ("general_add", MODEST_ICON_SIZE_BIG);
828         hildon_button_set_image (HILDON_BUTTON (priv->new_message_button), gtk_image_new_from_pixbuf (new_message_pixbuf));
829         g_object_unref (new_message_pixbuf);
830
831         gtk_box_pack_start (GTK_BOX (action_area_box), priv->new_message_button, TRUE, TRUE, 0);
832         gtk_widget_show_all (priv->new_message_button);
833         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), TRUE);
834
835         setup_menu (self);
836
837         priv->top_vbox = gtk_vbox_new (FALSE, 0);
838         gtk_container_add (GTK_CONTAINER (alignment), priv->contents_view);
839         gtk_box_pack_end (GTK_BOX (priv->top_vbox), alignment, TRUE, TRUE, 0);
840
841         gtk_container_add (GTK_CONTAINER (self), priv->top_vbox);
842
843         gtk_widget_show (alignment);
844         gtk_widget_show (priv->contents_view);
845         gtk_widget_show (priv->top_vbox);
846
847         connect_signals (MODEST_HEADER_WINDOW (self));
848
849         update_view (self, NULL);
850
851         /* Get device name */
852         modest_maemo_utils_get_device_name ();
853
854         app = hildon_program_get_instance ();
855         hildon_program_add_window (app, HILDON_WINDOW (self));
856
857         /* Set window icon */
858         window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG);
859         if (window_icon) {
860                 gtk_window_set_icon (GTK_WINDOW (self), window_icon);
861                 g_object_unref (window_icon);
862         }
863
864         /* Dont't restore settings here, 
865          * because it requires a gtk_widget_show(), 
866          * and we don't want to do that until later,
867          * so that the UI is not visible for non-menu D-Bus activation.
868          */
869
870         /* setup edit modes */
871         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE,
872                                                   _("mcen_ti_edit_delete"), _HL("wdgt_bd_delete"),
873                                                   GTK_TREE_VIEW (priv->header_view),
874                                                   GTK_SELECTION_MULTIPLE,
875                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_delete_message));
876         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE,
877                                                   _("mcen_ti_edit_move"), _HL("wdgt_bd_move"),
878                                                   GTK_TREE_VIEW (priv->header_view),
879                                                   GTK_SELECTION_MULTIPLE,
880                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_move_to));
881
882
883         modest_window_set_active_account (MODEST_WINDOW (self), account_name);
884         modest_window_set_active_mailbox (MODEST_WINDOW (self), mailbox);
885         mgr = modest_runtime_get_account_mgr ();
886         settings = modest_account_mgr_load_account_settings (mgr, account_name);
887         if (settings) {
888                 account_display_name = g_strdup (modest_account_settings_get_display_name (settings));
889                 store_settings = modest_account_settings_get_store_settings (settings);
890                 if (store_settings) {
891                         priv->current_store_account = 
892                                 g_strdup (modest_server_account_settings_get_account_name (store_settings));
893                         g_object_unref (store_settings);
894                 }
895                 g_object_unref (settings);
896         }
897         /* Set window title */
898         if (TNY_IS_FOLDER (folder)) {
899                 gchar *folder_name;
900
901                 if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
902                         const gchar *box_name;
903                         box_name = mailbox;
904                         if (box_name == NULL || box_name[0] == '\0') {
905                                 box_name = account_display_name;
906                         }
907                         folder_name = g_strconcat (_("mcen_me_folder_inbox"), " - ", box_name, NULL);
908                 } else {
909                         folder_name = modest_tny_folder_get_display_name (folder);
910                 }
911
912                 gtk_window_set_title (GTK_WINDOW (self), folder_name);
913                 g_free (folder_name);
914         }
915         g_free (account_display_name);
916
917
918         update_progress_hint (self);
919         update_sort_button (self);
920
921         return MODEST_WINDOW(self);
922 }
923
924 ModestHeaderView *
925 modest_header_window_get_header_view (ModestHeaderWindow *self)
926 {
927         ModestHeaderWindowPrivate *priv = NULL;
928
929         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
930
931         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
932         
933         return MODEST_HEADER_VIEW (priv->header_view);
934 }
935
936 static void setup_menu (ModestHeaderWindow *self)
937 {
938         ModestHeaderWindowPrivate *priv;
939
940         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
941         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
942
943         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
944                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
945                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
946         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_sendandreceive"), NULL,
947                                            APP_MENU_CALLBACK (modest_ui_actions_on_send_receive),
948                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_send_receive));
949         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
950                                            dngettext(GETTEXT_PACKAGE,
951                                                      "mcen_me_move_message",
952                                                      "mcen_me_move_messages",
953                                                      2),
954                                            NULL,
955                                            APP_MENU_CALLBACK (set_moveto_edit_mode),
956                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
957         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_delete_messages"), NULL,
958                                            APP_MENU_CALLBACK (set_delete_edit_mode),
959                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_delete));
960         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_folder_details"), NULL,
961                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
962                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
963         priv->sort_button = hildon_button_new (MODEST_EDITABLE_SIZE,
964                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL);
965         hildon_button_set_title (HILDON_BUTTON (priv->sort_button), _("mcen_me_sort"));
966         g_signal_connect_after (G_OBJECT (priv->sort_button), "clicked",
967                                 G_CALLBACK (modest_ui_actions_on_sort), (gpointer) self);
968         hildon_button_set_style(HILDON_BUTTON (priv->sort_button), HILDON_BUTTON_STYLE_PICKER);
969         hildon_button_set_title_alignment (HILDON_BUTTON (priv->sort_button), 0.5, 0.5);
970         hildon_button_set_value_alignment (HILDON_BUTTON (priv->sort_button), 0.5, 0.5);
971         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->sort_button),
972                                                   modest_ui_dimming_rules_on_sort);
973
974         priv->show_more_button = hildon_button_new (MODEST_EDITABLE_SIZE, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
975         hildon_button_set_title (HILDON_BUTTON (priv->show_more_button), _("mcen_va_more"));
976         hildon_button_set_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5, 1.0, 1.0);
977         hildon_button_set_title_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5);
978         hildon_button_set_value_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5);
979         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->show_more_button),
980                                                   NULL);
981         gtk_widget_hide_all (priv->show_more_button);
982
983         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_outbox_cancelsend"), NULL,
984                                            APP_MENU_CALLBACK (modest_ui_actions_cancel_send),
985                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_cancel_sending_all));
986 }
987
988 static void 
989 update_view (ModestHeaderWindow *self,
990              TnyFolderChange *change)
991 {
992         ModestHeaderWindowPrivate *priv = NULL;
993         gboolean refilter = FALSE;
994         gboolean folder_empty = FALSE;
995         gboolean all_marked_as_deleted = FALSE;
996         TnyFolder *folder;
997         gchar *show_more_value;
998         guint visible;
999         guint all_count;
1000
1001         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
1002
1003         /* It could happen when some event is received and the window
1004            was previously closed */
1005         if (!MODEST_IS_HEADER_WINDOW (self))
1006                 return;
1007
1008         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1009
1010         folder = modest_header_view_get_folder ((ModestHeaderView *) priv->header_view);
1011         if (!folder)
1012                 return;
1013
1014         if (change != NULL) {
1015                 TnyFolderChangeChanged changed;
1016
1017                 changed = tny_folder_change_get_changed (change);
1018                 /* If something changes */
1019                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1020                         all_count = (guint) tny_folder_change_get_new_all_count (change);
1021                 else
1022                         all_count = (guint) tny_folder_get_all_count (folder);
1023
1024                 folder_empty = (all_count == 0);
1025                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1026                         refilter = TRUE;
1027         } else {
1028                 all_count = (guint) tny_folder_get_all_count (folder);
1029                 folder_empty = (all_count == 0);
1030         }
1031         g_object_unref (folder);
1032
1033         /* Check if all messages are marked to be deleted */
1034         all_marked_as_deleted = modest_header_view_is_empty (MODEST_HEADER_VIEW (priv->header_view));
1035         folder_empty = folder_empty || all_marked_as_deleted;
1036
1037         /* Set style of headers view */
1038         set_contents_state (self, folder_empty?CONTENTS_STATE_EMPTY:CONTENTS_STATE_HEADERS);
1039
1040         if (refilter)
1041                 modest_header_view_refilter (MODEST_HEADER_VIEW (priv->header_view));
1042
1043         visible = modest_header_view_get_show_latest (MODEST_HEADER_VIEW (priv->header_view));
1044
1045         if (visible > 0 && all_count > 0 && visible < all_count && folder_empty) {
1046                 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (priv->header_view), visible + SHOW_LATEST_SIZE);
1047         }
1048
1049         if (visible > all_count)
1050                 visible = all_count;
1051         if (visible == 0 || visible == all_count) {
1052                 gtk_widget_hide_all (priv->show_more_button);
1053         } else {
1054                 gtk_widget_show_all (priv->show_more_button);
1055         }
1056         show_more_value = g_strdup_printf (_("mcen_va_more_toview"), visible, all_count);
1057
1058         hildon_button_set_value (HILDON_BUTTON (priv->show_more_button),
1059                                  show_more_value);
1060 }
1061
1062 static void 
1063 set_contents_state (ModestHeaderWindow *self, 
1064                     ContentsState state)
1065 {
1066         ModestHeaderWindowPrivate *priv = NULL;
1067
1068         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
1069         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1070
1071         if (priv->contents_state == state)
1072                 return;
1073
1074         /* Remove from container the old content */
1075         switch (priv->contents_state) {
1076         case CONTENTS_STATE_EMPTY:
1077                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->empty_view);
1078                 break;
1079         case CONTENTS_STATE_HEADERS:
1080                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->header_view);
1081                 break;
1082         case CONTENTS_STATE_NONE:
1083                 break;
1084         }
1085
1086         /* Add the new content */
1087         switch (state) {
1088         case CONTENTS_STATE_EMPTY:
1089                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->empty_view);
1090                 gtk_widget_show (priv->empty_view);
1091                 break;
1092         case CONTENTS_STATE_HEADERS:
1093                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->header_view);
1094                 gtk_widget_show (priv->header_view);
1095                 break;
1096         case CONTENTS_STATE_NONE:
1097                 break;
1098         }
1099         priv->contents_state = state;
1100 }
1101
1102 static void
1103 on_msg_count_changed (ModestHeaderView *header_view,
1104                       TnyFolder *folder,
1105                       TnyFolderChange *change,
1106                       ModestHeaderWindow *header_window)
1107 {
1108         g_return_if_fail (MODEST_IS_HEADER_WINDOW (header_window));
1109
1110         update_view (MODEST_HEADER_WINDOW (header_window), change);
1111 }
1112
1113 static void 
1114 on_header_activated (ModestHeaderView *header_view,
1115                      TnyHeader *header,
1116                      GtkTreePath *path,
1117                      ModestHeaderWindow *header_window)
1118 {
1119         modest_ui_actions_on_header_activated (header_view, header, path, MODEST_WINDOW (header_window));
1120 }
1121
1122 static void
1123 updating_banner_destroyed (gpointer data,
1124                            GObject *where_the_object_was)
1125 {
1126         ModestHeaderWindowPrivate *priv = NULL;
1127
1128         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
1129
1130         priv->updating_banner = NULL;
1131 }
1132
1133 static gboolean
1134 show_updating_banner (gpointer user_data)
1135 {
1136         ModestHeaderWindowPrivate *priv = NULL;
1137
1138         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1139
1140         if (priv->updating_banner == NULL) {
1141
1142                 /* We're outside the main lock */
1143                 gdk_threads_enter ();
1144                 priv->updating_banner = 
1145                         modest_platform_animation_banner (GTK_WIDGET (user_data), NULL,
1146                                                           _CS ("ckdg_pb_updating"));
1147
1148                 /* We need this because banners in Maemo could be
1149                    destroyed by dialogs so we need to properly update
1150                    our reference to it */
1151                 g_object_weak_ref (G_OBJECT (priv->updating_banner),
1152                                    updating_banner_destroyed,
1153                                    user_data);
1154                 gdk_threads_leave ();
1155         }
1156
1157         /* Remove timeout */
1158         priv->updating_banner_timeout = 0;
1159         return FALSE;
1160 }
1161
1162 /**
1163  * We use this function to show/hide a progress banner showing
1164  * "Updating" while the header view is being filled. We're not showing
1165  * it unless the update takes more than 2 seconds
1166  *
1167  * If starting = TRUE then the refresh is starting, otherwise it means
1168  * that is has just finished
1169  */
1170 static void 
1171 on_updating_msg_list (ModestHeaderView *header_view,
1172                       gboolean starting,
1173                       gpointer user_data)
1174 {
1175         ModestHeaderWindowPrivate *priv = NULL;
1176
1177         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1178         
1179         /* Remove old timeout */
1180         if (priv->updating_banner_timeout > 0) {
1181                 g_source_remove (priv->updating_banner_timeout);
1182                 priv->updating_banner_timeout = 0;
1183         }
1184
1185         /* Create a new timeout */
1186         if (starting) {
1187                 priv->updating_banner_timeout = 
1188                         g_timeout_add (2000, show_updating_banner, user_data);
1189         } else {
1190                 /* Remove the banner if exists */
1191                 if (priv->updating_banner) {
1192                         gtk_widget_destroy (priv->updating_banner);
1193                         priv->updating_banner = NULL;
1194                 }
1195         }
1196 }
1197
1198 static void
1199 set_delete_edit_mode (GtkButton *button,
1200                       ModestHeaderWindow *self)
1201 {
1202         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE);
1203 }
1204
1205 static void
1206 set_moveto_edit_mode (GtkButton *button,
1207                     ModestHeaderWindow *self)
1208 {
1209         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE);
1210 }
1211
1212 static gboolean 
1213 on_expose_event(GtkTreeView *header_view,
1214                 GdkEventExpose *event,
1215                 gpointer user_data)
1216 {
1217         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
1218         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1219
1220         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1221
1222         if (priv->autoscroll)
1223                 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (priv->contents_view), 0.0, 0.0);
1224
1225         return FALSE;
1226 }
1227
1228 static gboolean 
1229 on_map_event(GtkWidget *widget,
1230              GdkEvent *event,
1231              gpointer user_data)
1232 {
1233         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
1234         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1235
1236         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1237
1238         if (priv->progress_hint) {
1239                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
1240         }
1241         return FALSE;
1242 }
1243
1244 static void
1245 on_progress_list_changed (ModestWindowMgr *mgr,
1246                           ModestHeaderWindow *self)
1247 {
1248         update_progress_hint (self);
1249 }
1250
1251 static gboolean
1252 has_active_operations (ModestHeaderWindow *self)
1253 {
1254         GSList *operations = NULL, *node;
1255         ModestMailOperationQueue *queue;
1256         gboolean has_active = FALSE;
1257
1258         queue = modest_runtime_get_mail_operation_queue ();
1259         operations = modest_mail_operation_queue_get_by_source (queue, G_OBJECT (self));
1260
1261         for (node = operations; node != NULL; node = g_slist_next (node)) {
1262                 if (!modest_mail_operation_is_finished (MODEST_MAIL_OPERATION (node->data))) {
1263                         has_active = TRUE;
1264                         break;
1265                 }
1266         }
1267
1268         if (operations) {
1269                 g_slist_foreach (operations, (GFunc) g_object_unref, NULL);
1270                 g_slist_free (operations);
1271         }
1272
1273         return has_active;
1274 }
1275
1276 static void
1277 update_progress_hint (ModestHeaderWindow *self)
1278 {
1279         ModestHeaderWindowPrivate *priv;
1280
1281         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1282
1283         priv->progress_hint = FALSE;
1284
1285         if (has_active_operations (self)) {
1286                 priv->progress_hint = TRUE;
1287         } else {
1288                 priv->progress_hint = FALSE;
1289         }
1290
1291         if (!priv->progress_hint && priv->current_store_account) {
1292                 priv->progress_hint = 
1293                         modest_window_mgr_has_progress_operation_on_account (modest_runtime_get_window_mgr (),
1294                                                                              priv->current_store_account);
1295         }
1296
1297         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1298
1299         if (GTK_WIDGET_VISIBLE (self)) {
1300                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), priv->progress_hint?1:0);
1301         }
1302 }
1303
1304 gboolean
1305 modest_header_window_toolbar_on_transfer_mode     (ModestHeaderWindow *self)
1306 {
1307         ModestHeaderWindowPrivate *priv= NULL; 
1308
1309         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1310         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1311
1312         return priv->progress_hint;
1313 }
1314
1315 gboolean 
1316 modest_header_window_transfer_mode_enabled (ModestHeaderWindow *self)
1317 {
1318         ModestHeaderWindowPrivate *priv;
1319         
1320         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);   
1321         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
1322
1323         return priv->progress_hint;
1324 }
1325
1326 static void 
1327 on_mail_operation_started (ModestMailOperation *mail_op,
1328                            gpointer user_data)
1329 {
1330         ModestHeaderWindow *self;
1331         ModestMailOperationTypeOperation op_type;
1332         GObject *source = NULL;
1333
1334         self = MODEST_HEADER_WINDOW (user_data);
1335         op_type = modest_mail_operation_get_type_operation (mail_op);
1336         source = modest_mail_operation_get_source(mail_op);
1337         if (G_OBJECT (self) == source) {
1338                 update_progress_hint (self);
1339         }
1340         g_object_unref (source);
1341 }
1342
1343 static void 
1344 on_mail_operation_finished (ModestMailOperation *mail_op,
1345                             gpointer user_data)
1346 {
1347         ModestHeaderWindow *self;
1348
1349         self = MODEST_HEADER_WINDOW (user_data);
1350
1351         /* Don't disable the progress hint if there are more pending
1352            operations from this window */
1353         update_progress_hint (self);
1354
1355         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1356 }
1357
1358 static void
1359 on_queue_changed (ModestMailOperationQueue *queue,
1360                   ModestMailOperation *mail_op,
1361                   ModestMailOperationQueueNotification type,
1362                   ModestHeaderWindow *self)
1363 {
1364         ModestHeaderWindowPrivate *priv;
1365
1366         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1367
1368         /* If this operations was created by another window, do nothing */
1369         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1370                 return;
1371
1372         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
1373                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1374                                                                G_OBJECT (mail_op),
1375                                                                "operation-started",
1376                                                                G_CALLBACK (on_mail_operation_started),
1377                                                                self);
1378                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1379                                                                G_OBJECT (mail_op),
1380                                                                "operation-finished",
1381                                                                G_CALLBACK (on_mail_operation_finished),
1382                                                                self);
1383         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
1384                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1385                                                                   G_OBJECT (mail_op),
1386                                                                   "operation-started");
1387                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1388                                                                   G_OBJECT (mail_op),
1389                                                                   "operation-finished");
1390         }
1391 }
1392
1393 static void
1394 modest_header_window_pack_toolbar (ModestHildon2Window *self,
1395                                    GtkPackType pack_type,
1396                                    GtkWidget *toolbar)
1397 {
1398         ModestHeaderWindowPrivate *priv;
1399
1400         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
1401         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1402
1403         if (pack_type == GTK_PACK_START) {
1404                 gtk_box_pack_start (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
1405         } else {
1406                 gtk_box_pack_end (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
1407         }
1408 }
1409
1410 static void 
1411 edit_mode_changed (ModestHeaderWindow *header_window,
1412                    gint edit_mode_id,
1413                    gboolean enabled,
1414                    ModestHeaderWindow *self)
1415 {
1416         ModestHeaderWindowPrivate *priv;
1417         ModestHeaderViewFilter filter = MODEST_HEADER_VIEW_FILTER_NONE;
1418
1419         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
1420         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1421
1422         switch (edit_mode_id) {
1423         case EDIT_MODE_COMMAND_MOVE:
1424                 filter = MODEST_HEADER_VIEW_FILTER_MOVEABLE;
1425                 break;
1426         case EDIT_MODE_COMMAND_DELETE:
1427                 filter = MODEST_HEADER_VIEW_FILTER_DELETABLE;
1428                 break;
1429         case MODEST_HILDON2_WINDOW_EDIT_MODE_NONE:
1430                 filter = MODEST_HEADER_VIEW_FILTER_NONE;
1431                 break;
1432         }
1433
1434         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), !enabled);
1435         if (enabled) {
1436                 modest_header_view_set_filter (MODEST_HEADER_VIEW (priv->header_view), 
1437                                                filter);
1438         } else {
1439                 GtkTreeSelection *sel;
1440
1441                 /* Unselect all. This will prevent us from keeping a
1442                    reference to a TnyObject that we don't want to
1443                    have */
1444                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->header_view));
1445                 gtk_tree_selection_unselect_all (sel);
1446
1447                 modest_header_view_unset_filter (MODEST_HEADER_VIEW (priv->header_view), 
1448                                                  filter);
1449         }
1450 }
1451
1452 static void 
1453 on_sort_column_changed (GtkTreeSortable *treesortable,
1454                         gpointer         user_data)
1455 {
1456         update_sort_button (MODEST_HEADER_WINDOW (user_data));
1457 }
1458
1459 static void
1460 update_sort_button (ModestHeaderWindow *self)
1461 {
1462         ModestHeaderWindowPrivate *priv;
1463         GtkTreeSortable *sortable;
1464         gint current_sort_colid = -1;
1465         GtkSortType current_sort_type;
1466         const gchar *value = NULL;
1467
1468         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1469
1470         /* This could happen as the first time the model is set the
1471            header_view is still not assigned to priv->header_view */
1472         if (!priv->header_view)
1473                 return;
1474
1475         sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->header_view)));
1476
1477         if (!gtk_tree_sortable_get_sort_column_id (sortable,
1478                                                    &current_sort_colid, &current_sort_type)) {
1479                 value =  _("mcen_li_sort_sender_date_newest");
1480         } else {
1481                 switch (current_sort_colid) {
1482                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
1483                 {
1484                         GList *cols = NULL;
1485                         cols = modest_header_view_get_columns (MODEST_HEADER_VIEW (priv->header_view));
1486                         if (cols != NULL) {
1487                                 gpointer flags_sort_type_pointer;
1488                                 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), 
1489                                                                              MODEST_HEADER_VIEW_FLAG_SORT);
1490                                 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
1491                                         value = _("mcen_li_sort_priority");
1492                                 else
1493                                         value = _("mcen_li_sort_attachment");
1494                                 g_list_free(cols);      
1495                         }
1496                 } 
1497                 break;
1498                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
1499                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
1500                         if (current_sort_type == GTK_SORT_ASCENDING)
1501                                 value = _("mcen_li_sort_sender_recipient_az");
1502                         else
1503                                 value = _("mcen_li_sort_sender_recipient_za");
1504                         break;
1505                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
1506                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
1507                         if (current_sort_type == GTK_SORT_ASCENDING)
1508                                 value = _("mcen_li_sort_date_oldest");
1509                         else
1510                                 value = _("mcen_li_sort_date_newest");
1511                         break;
1512                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
1513                         if (current_sort_type == GTK_SORT_ASCENDING)
1514                                 value = _("mcen_li_sort_subject_az");
1515                         else
1516                                 value = _("mcen_li_sort_subject_za");
1517                         break;
1518                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
1519                         if (current_sort_type == GTK_SORT_ASCENDING)
1520                                 value = _("mcen_li_sort_size_smallest");
1521                         else
1522                                 value = _("mcen_li_sort_size_largest");
1523                         break;
1524                 } 
1525         }
1526
1527         hildon_button_set_value (HILDON_BUTTON (priv->sort_button), value?value:"");
1528 }
1529
1530 static void
1531 on_horizontal_movement (HildonPannableArea *hildonpannable,
1532                         gint                direction,
1533                         gdouble             initial_x,
1534                         gdouble             initial_y,
1535                         gpointer            user_data)
1536 {
1537         ModestHeaderWindowPrivate *priv;
1538         gint dest_x, dest_y;
1539         TnyHeader *header;
1540
1541         /* Ignore right to left movement */
1542         if (direction == HILDON_MOVEMENT_LEFT)
1543                 return;
1544
1545         /* Get the header to delete */
1546         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1547
1548         /* Get tree view coordinates */
1549         if (!gtk_widget_translate_coordinates ((GtkWidget *) hildonpannable,
1550                                                priv->header_view,
1551                                                initial_x,
1552                                                initial_y,
1553                                                &dest_x,
1554                                                &dest_y))
1555             return;
1556
1557         header = modest_header_view_get_header_at_pos ((ModestHeaderView *) priv->header_view,
1558                                                        dest_x, dest_y);
1559         if (header) {
1560                 delete_header ((GtkWindow *) user_data, header);
1561                 g_object_unref (header);
1562         }
1563 }
1564
1565
1566 static gboolean
1567 on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
1568 {
1569         ModestHeaderWindowPrivate *priv;
1570         HildonPannableArea *pannable;
1571         /* FIXME: set scroll_speed depends on for how long the key was pressed */
1572         gint scroll_speed = 3;
1573
1574         if (event->type == GDK_KEY_RELEASE)
1575                 return FALSE;
1576
1577         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(user_data);
1578
1579         pannable = HILDON_PANNABLE_AREA (priv->contents_view);
1580
1581         switch (event->keyval) {
1582
1583         case GDK_Up:
1584                 priv->autoscroll = FALSE;
1585                 modest_maemo_utils_scroll_pannable(pannable, 0, -scroll_speed);
1586                 break;
1587
1588         case GDK_Down:
1589                 priv->autoscroll = FALSE;
1590                 modest_maemo_utils_scroll_pannable(pannable, 0, scroll_speed);
1591                 break;
1592         }
1593
1594         return FALSE;
1595 }
1596
1597 static void
1598 modest_header_window_show_more (GtkAction *action, ModestWindow *win)
1599 {
1600         ModestHeaderWindow *self;
1601         ModestHeaderWindowPrivate *priv = NULL;
1602
1603         self = MODEST_HEADER_WINDOW (win);
1604         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1605         if (!priv->header_view)
1606                 return;
1607         
1608         if (modest_header_view_get_not_latest (MODEST_HEADER_VIEW (priv->header_view)) > 0) {
1609                 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (priv->header_view),
1610                                                     modest_header_view_get_show_latest (MODEST_HEADER_VIEW (priv->header_view)) + 
1611                                                     SHOW_LATEST_SIZE);
1612                 update_view (self, NULL);
1613         }
1614 }