Avoid wait on taking screenshot. Now it shouldn't be required.
[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         }
668         modest_header_view_set_show_latest (MODEST_HEADER_VIEW (header_view), limit_headers?SHOW_LATEST_SIZE:0);
669
670         priv->notify_model = g_signal_connect ((GObject*) header_view, "notify::model",
671                                                G_CALLBACK (on_header_view_model_changed), self);
672
673         modest_header_view_set_folder (MODEST_HEADER_VIEW (header_view), folder,
674                                        TRUE, self, folder_refreshed_cb, self);
675         modest_header_view_set_filter (MODEST_HEADER_VIEW (header_view),
676                                        MODEST_HEADER_VIEW_FILTER_NONE);
677         modest_widget_memory_restore (modest_runtime_get_conf (), G_OBJECT(header_view),
678                                       MODEST_CONF_HEADER_VIEW_KEY);
679
680         /* Create CSM menu */
681         priv->csm_menu = gtk_menu_new ();
682         delete_item = gtk_menu_item_new_with_label (_HL("wdgt_bd_delete"));
683         mark_read_item = gtk_menu_item_new_with_label (_("mcen_me_inbox_mark_as_read"));
684         mark_unread_item = gtk_menu_item_new_with_label (_("mcen_me_inbox_mark_as_unread"));
685         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), delete_item);
686         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), mark_read_item);
687         gtk_menu_shell_append (GTK_MENU_SHELL (priv->csm_menu), mark_unread_item);
688         hildon_gtk_widget_set_theme_size (delete_item, MODEST_EDITABLE_SIZE);
689         hildon_gtk_widget_set_theme_size (mark_unread_item, MODEST_EDITABLE_SIZE);
690         hildon_gtk_widget_set_theme_size (mark_read_item, MODEST_EDITABLE_SIZE);
691         gtk_widget_show_all (priv->csm_menu);
692
693         /* Connect signals */
694         g_signal_connect ((GObject *) header_view, "tap-and-hold-query",
695                           G_CALLBACK (tap_and_hold_query_cb), self);
696         g_signal_connect ((GObject *) delete_item, "activate",
697                           G_CALLBACK (on_delete_csm_activated), self);
698         g_signal_connect ((GObject *) mark_read_item, "activate",
699                           G_CALLBACK (on_mark_read_csm_activated), self);
700         g_signal_connect ((GObject *) mark_unread_item, "activate",
701                           G_CALLBACK (on_mark_unread_csm_activated), self);
702
703         /* Add tap&hold handling */
704         gtk_widget_tap_and_hold_setup (header_view, priv->csm_menu, NULL, 0);
705
706         return header_view;
707 }
708
709 static GtkWidget *
710 create_empty_view (ModestWindow *self)
711 {
712         GtkWidget *viewport = NULL;
713         GtkWidget *label = NULL;
714         GtkWidget *align = NULL;
715         GtkWidget *vbox = NULL;
716         GtkWidget *hbox = NULL;
717         GtkWidget *button = NULL;
718         GdkPixbuf *new_message_pixbuf;
719         ModestHeaderWindowPrivate *priv;
720
721         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
722
723         vbox = gtk_vbox_new (0, FALSE);
724
725         align = gtk_alignment_new(EMPTYVIEW_XALIGN, EMPTYVIEW_YALIGN, EMPTYVIEW_XSPACE, EMPTYVIEW_YSPACE);
726         label = gtk_label_new (_("mcen_ia_nomessages"));
727         hildon_helper_set_logical_font (label, "LargeSystemFont");
728         gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
729         gtk_widget_show (label);
730         gtk_widget_show (align);
731         gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);  
732         gtk_container_add (GTK_CONTAINER (align), label);
733         gtk_box_pack_end (GTK_BOX (vbox), align, TRUE, TRUE, 0);
734
735         button = hildon_button_new (MODEST_EDITABLE_SIZE, 
736                                     HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
737
738         hildon_button_set_title (HILDON_BUTTON (button), _("mcen_ti_new_message"));
739         new_message_pixbuf = modest_platform_get_icon ("general_add", MODEST_ICON_SIZE_BIG);
740         hildon_button_set_image (HILDON_BUTTON (button), 
741                                  gtk_image_new_from_pixbuf (new_message_pixbuf));
742         g_object_unref (new_message_pixbuf);
743         gtk_widget_show_all (button);
744
745         hbox = gtk_hbox_new (TRUE, 0);
746         gtk_widget_show (hbox);
747         gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
748         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
749
750         gtk_widget_show (vbox);
751
752         g_signal_connect (button,
753                           "clicked",
754                           G_CALLBACK (modest_ui_actions_on_new_msg), self);
755
756         viewport = gtk_viewport_new ((GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 0, 0, 0), 
757                                      (GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
758         gtk_container_add (GTK_CONTAINER (viewport), vbox);
759
760         return viewport;
761 }
762
763 static void
764 on_vertical_movement (HildonPannableArea *area,
765                       HildonMovementDirection direction,
766                       gdouble x, gdouble y, gpointer user_data)
767 {
768         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
769         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
770
771         priv->autoscroll = FALSE;
772 }
773
774
775 ModestWindow *
776 modest_header_window_new (TnyFolder *folder, const gchar *account_name, const gchar *mailbox)
777 {
778         ModestHeaderWindow *self = NULL;        
779         ModestHeaderWindowPrivate *priv = NULL;
780         HildonProgram *app;
781         GdkPixbuf *window_icon;
782         ModestAccountMgr *mgr;
783         ModestAccountSettings *settings = NULL;
784         ModestServerAccountSettings *store_settings = NULL;
785         GtkWidget *action_area_box;
786         GdkPixbuf *new_message_pixbuf;
787         GtkWidget *alignment;
788         gchar *account_display_name = NULL;
789         
790         self  = MODEST_HEADER_WINDOW(g_object_new(MODEST_TYPE_HEADER_WINDOW, NULL));
791         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
792
793         priv->contents_view = hildon_pannable_area_new ();
794         alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
795         gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
796                                    HILDON_MARGIN_HALF, 0,
797                                    HILDON_MARGIN_DOUBLE, HILDON_MARGIN_DOUBLE);
798
799         /* We need to do this here to properly listen for mail
800            operations because create_header_view launches a mail
801            operation */
802         priv->queue_change_handler =
803                 g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
804                                   "queue-changed",
805                                   G_CALLBACK (on_queue_changed),
806                                   self);
807
808         priv->header_view  = create_header_view (MODEST_WINDOW (self), folder);
809         priv->empty_view = create_empty_view (MODEST_WINDOW (self));
810
811         /* Transform the floating reference in a "hard" reference. We
812            need to do this because the widgets could be added/removed
813            to containers many times so we always need to keep a
814            reference. It could happen also that some widget is never
815            added to any container */
816         g_object_ref_sink (priv->header_view);
817         g_object_ref_sink (priv->empty_view);
818
819         g_signal_connect (G_OBJECT (self), "edit-mode-changed",
820                           G_CALLBACK (edit_mode_changed), (gpointer) self);
821
822         action_area_box = hildon_tree_view_get_action_area_box (GTK_TREE_VIEW (priv->header_view));
823         priv->new_message_button = hildon_button_new (MODEST_EDITABLE_SIZE, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
824
825         hildon_button_set_title (HILDON_BUTTON (priv->new_message_button), _("mcen_ti_new_message"));
826         new_message_pixbuf = modest_platform_get_icon ("general_add", MODEST_ICON_SIZE_BIG);
827         hildon_button_set_image (HILDON_BUTTON (priv->new_message_button), gtk_image_new_from_pixbuf (new_message_pixbuf));
828         g_object_unref (new_message_pixbuf);
829
830         gtk_box_pack_start (GTK_BOX (action_area_box), priv->new_message_button, TRUE, TRUE, 0);
831         gtk_widget_show_all (priv->new_message_button);
832         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), TRUE);
833         
834         setup_menu (self);
835
836         priv->top_vbox = gtk_vbox_new (FALSE, 0);
837         gtk_container_add (GTK_CONTAINER (alignment), priv->contents_view);
838         gtk_box_pack_end (GTK_BOX (priv->top_vbox), alignment, TRUE, TRUE, 0);
839
840         gtk_container_add (GTK_CONTAINER (self), priv->top_vbox);
841
842         gtk_widget_show (alignment);
843         gtk_widget_show (priv->contents_view);
844         gtk_widget_show (priv->top_vbox);
845
846         connect_signals (MODEST_HEADER_WINDOW (self));
847
848         update_view (self, NULL);
849
850         /* Get device name */
851         modest_maemo_utils_get_device_name ();
852
853         app = hildon_program_get_instance ();
854         hildon_program_add_window (app, HILDON_WINDOW (self));
855
856         /* Set window icon */
857         window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG);
858         if (window_icon) {
859                 gtk_window_set_icon (GTK_WINDOW (self), window_icon);
860                 g_object_unref (window_icon);
861         }
862
863         /* Dont't restore settings here, 
864          * because it requires a gtk_widget_show(), 
865          * and we don't want to do that until later,
866          * so that the UI is not visible for non-menu D-Bus activation.
867          */
868
869         /* setup edit modes */
870         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE,
871                                                   _("mcen_ti_edit_delete"), _HL("wdgt_bd_delete"),
872                                                   GTK_TREE_VIEW (priv->header_view),
873                                                   GTK_SELECTION_MULTIPLE,
874                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_delete_message));
875         modest_hildon2_window_register_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE,
876                                                   _("mcen_ti_edit_move"), _HL("wdgt_bd_move"),
877                                                   GTK_TREE_VIEW (priv->header_view),
878                                                   GTK_SELECTION_MULTIPLE,
879                                                   EDIT_MODE_CALLBACK (modest_ui_actions_on_edit_mode_move_to));
880
881
882         modest_window_set_active_account (MODEST_WINDOW (self), account_name);
883         modest_window_set_active_mailbox (MODEST_WINDOW (self), mailbox);
884         mgr = modest_runtime_get_account_mgr ();
885         settings = modest_account_mgr_load_account_settings (mgr, account_name);
886         if (settings) {
887                 account_display_name = g_strdup (modest_account_settings_get_display_name (settings));
888                 store_settings = modest_account_settings_get_store_settings (settings);
889                 if (store_settings) {
890                         priv->current_store_account = 
891                                 g_strdup (modest_server_account_settings_get_account_name (store_settings));
892                         g_object_unref (store_settings);
893                 }
894                 g_object_unref (settings);
895         }
896         /* Set window title */
897         if (TNY_IS_FOLDER (folder)) {
898                 gchar *folder_name;
899
900                 if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
901                         const gchar *box_name;
902                         box_name = mailbox;
903                         if (box_name == NULL || box_name[0] == '\0') {
904                                 box_name = account_display_name;
905                         }
906                         folder_name = g_strconcat (_("mcen_me_folder_inbox"), " - ", box_name, NULL);
907                 } else {
908                         folder_name = modest_tny_folder_get_display_name (folder);
909                 }
910                 
911                 gtk_window_set_title (GTK_WINDOW (self), folder_name);
912                 g_free (folder_name);
913         }
914         g_free (account_display_name);
915
916
917         update_progress_hint (self);
918         update_sort_button (self);
919
920         return MODEST_WINDOW(self);
921 }
922
923 ModestHeaderView *
924 modest_header_window_get_header_view (ModestHeaderWindow *self)
925 {
926         ModestHeaderWindowPrivate *priv = NULL;
927
928         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW(self), FALSE);
929
930         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
931         
932         return MODEST_HEADER_VIEW (priv->header_view);
933 }
934
935 static void setup_menu (ModestHeaderWindow *self)
936 {
937         ModestHeaderWindowPrivate *priv;
938
939         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
940         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
941
942         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
943                                            APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
944                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
945         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_sendandreceive"), NULL,
946                                            APP_MENU_CALLBACK (modest_ui_actions_on_send_receive),
947                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_send_receive));
948         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
949                                            dngettext(GETTEXT_PACKAGE,
950                                                      "mcen_me_move_message",
951                                                      "mcen_me_move_messages",
952                                                      2),
953                                            NULL,
954                                            APP_MENU_CALLBACK (set_moveto_edit_mode),
955                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
956         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_delete_messages"), NULL,
957                                            APP_MENU_CALLBACK (set_delete_edit_mode),
958                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_delete));
959         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_folder_details"), NULL,
960                                            APP_MENU_CALLBACK (modest_ui_actions_on_details),
961                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
962         priv->sort_button = hildon_button_new (MODEST_EDITABLE_SIZE,
963                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL);
964         hildon_button_set_title (HILDON_BUTTON (priv->sort_button), _("mcen_me_sort"));
965         g_signal_connect_after (G_OBJECT (priv->sort_button), "clicked",
966                                 G_CALLBACK (modest_ui_actions_on_sort), (gpointer) self);
967         hildon_button_set_style(HILDON_BUTTON (priv->sort_button), HILDON_BUTTON_STYLE_PICKER);
968         hildon_button_set_title_alignment (HILDON_BUTTON (priv->sort_button), 0.5, 0.5);
969         hildon_button_set_value_alignment (HILDON_BUTTON (priv->sort_button), 0.5, 0.5);
970         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->sort_button),
971                                                   modest_ui_dimming_rules_on_sort);
972
973         priv->show_more_button = hildon_button_new (MODEST_EDITABLE_SIZE, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
974         hildon_button_set_title (HILDON_BUTTON (priv->show_more_button), _("mcen_va_more"));
975         hildon_button_set_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5, 1.0, 1.0);
976         hildon_button_set_title_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5);
977         hildon_button_set_value_alignment (HILDON_BUTTON (priv->show_more_button), 0.5, 0.5);
978         modest_hildon2_window_add_button_to_menu (MODEST_HILDON2_WINDOW (self), GTK_BUTTON (priv->show_more_button),
979                                                   NULL);
980         gtk_widget_hide_all (priv->show_more_button);
981
982         modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_outbox_cancelsend"), NULL,
983                                            APP_MENU_CALLBACK (modest_ui_actions_cancel_send),
984                                            MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_cancel_sending_all));
985 }
986
987 static void 
988 update_view (ModestHeaderWindow *self,
989              TnyFolderChange *change)
990 {
991         ModestHeaderWindowPrivate *priv = NULL;
992         gboolean refilter = FALSE;
993         gboolean folder_empty = FALSE;
994         gboolean all_marked_as_deleted = FALSE;
995         TnyFolder *folder;
996         gchar *show_more_value;
997         guint visible;
998         guint all_count;
999
1000         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
1001
1002         /* It could happen when some event is received and the window
1003            was previously closed */
1004         if (!MODEST_IS_HEADER_WINDOW (self))
1005                 return;
1006
1007         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1008
1009         folder = modest_header_view_get_folder ((ModestHeaderView *) priv->header_view);
1010         if (!folder)
1011                 return;
1012
1013         if (change != NULL) {
1014                 TnyFolderChangeChanged changed;
1015
1016                 changed = tny_folder_change_get_changed (change);
1017                 /* If something changes */
1018                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1019                         all_count = (guint) tny_folder_change_get_new_all_count (change);
1020                 else
1021                         all_count = (guint) tny_folder_get_all_count (folder);
1022
1023                 folder_empty = (all_count == 0);
1024                 if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1025                         refilter = TRUE;
1026         } else {
1027                 all_count = (guint) tny_folder_get_all_count (folder);
1028                 folder_empty = (all_count == 0);
1029         }
1030         g_object_unref (folder);
1031
1032         /* Check if all messages are marked to be deleted */
1033         all_marked_as_deleted = modest_header_view_is_empty (MODEST_HEADER_VIEW (priv->header_view));
1034         folder_empty = folder_empty || all_marked_as_deleted;
1035
1036         /* Set style of headers view */
1037         set_contents_state (self, folder_empty?CONTENTS_STATE_EMPTY:CONTENTS_STATE_HEADERS);
1038
1039         if (refilter)
1040                 modest_header_view_refilter (MODEST_HEADER_VIEW (priv->header_view));
1041
1042         visible = modest_header_view_get_show_latest (MODEST_HEADER_VIEW (priv->header_view));
1043
1044         if (visible > 0 && all_count > 0 && visible < all_count && folder_empty) {
1045                 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (priv->header_view), visible + SHOW_LATEST_SIZE);
1046         }
1047
1048         if (visible > all_count)
1049                 visible = all_count;
1050         if (visible == 0 || visible == all_count) {
1051                 gtk_widget_hide_all (priv->show_more_button);
1052         } else {
1053                 gtk_widget_show_all (priv->show_more_button);
1054         }
1055         show_more_value = g_strdup_printf (_("mcen_va_more_toview"), visible, all_count);
1056
1057         hildon_button_set_value (HILDON_BUTTON (priv->show_more_button),
1058                                  show_more_value);
1059 }
1060
1061 static void 
1062 set_contents_state (ModestHeaderWindow *self, 
1063                     ContentsState state)
1064 {
1065         ModestHeaderWindowPrivate *priv = NULL;
1066
1067         g_return_if_fail (MODEST_IS_HEADER_WINDOW(self));
1068         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1069
1070         if (priv->contents_state == state)
1071                 return;
1072
1073         /* Remove from container the old content */
1074         switch (priv->contents_state) {
1075         case CONTENTS_STATE_EMPTY:
1076                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->empty_view);
1077                 break;
1078         case CONTENTS_STATE_HEADERS:
1079                 gtk_container_remove (GTK_CONTAINER (priv->contents_view), priv->header_view);
1080                 break;
1081         case CONTENTS_STATE_NONE:
1082                 break;
1083         }
1084
1085         /* Add the new content */
1086         switch (state) {
1087         case CONTENTS_STATE_EMPTY:
1088                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->empty_view);
1089                 gtk_widget_show (priv->empty_view);
1090                 break;
1091         case CONTENTS_STATE_HEADERS:
1092                 gtk_container_add (GTK_CONTAINER (priv->contents_view), priv->header_view);
1093                 gtk_widget_show (priv->header_view);
1094                 break;
1095         case CONTENTS_STATE_NONE:
1096                 break;
1097         }
1098         priv->contents_state = state;
1099 }
1100
1101 static void
1102 on_msg_count_changed (ModestHeaderView *header_view,
1103                       TnyFolder *folder,
1104                       TnyFolderChange *change,
1105                       ModestHeaderWindow *header_window)
1106 {
1107         g_return_if_fail (MODEST_IS_HEADER_WINDOW (header_window));
1108
1109         update_view (MODEST_HEADER_WINDOW (header_window), change);
1110 }
1111
1112 static void 
1113 on_header_activated (ModestHeaderView *header_view,
1114                      TnyHeader *header,
1115                      GtkTreePath *path,
1116                      ModestHeaderWindow *header_window)
1117 {
1118         modest_ui_actions_on_header_activated (header_view, header, path, MODEST_WINDOW (header_window));
1119 }
1120
1121 static void
1122 updating_banner_destroyed (gpointer data,
1123                            GObject *where_the_object_was)
1124 {
1125         ModestHeaderWindowPrivate *priv = NULL;
1126
1127         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (data);
1128
1129         priv->updating_banner = NULL;
1130 }
1131
1132 static gboolean
1133 show_updating_banner (gpointer user_data)
1134 {
1135         ModestHeaderWindowPrivate *priv = NULL;
1136
1137         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1138
1139         if (priv->updating_banner == NULL) {
1140
1141                 /* We're outside the main lock */
1142                 gdk_threads_enter ();
1143                 priv->updating_banner = 
1144                         modest_platform_animation_banner (GTK_WIDGET (user_data), NULL,
1145                                                           _CS ("ckdg_pb_updating"));
1146
1147                 /* We need this because banners in Maemo could be
1148                    destroyed by dialogs so we need to properly update
1149                    our reference to it */
1150                 g_object_weak_ref (G_OBJECT (priv->updating_banner),
1151                                    updating_banner_destroyed,
1152                                    user_data);
1153                 gdk_threads_leave ();
1154         }
1155
1156         /* Remove timeout */
1157         priv->updating_banner_timeout = 0;
1158         return FALSE;
1159 }
1160
1161 /**
1162  * We use this function to show/hide a progress banner showing
1163  * "Updating" while the header view is being filled. We're not showing
1164  * it unless the update takes more than 2 seconds
1165  *
1166  * If starting = TRUE then the refresh is starting, otherwise it means
1167  * that is has just finished
1168  */
1169 static void 
1170 on_updating_msg_list (ModestHeaderView *header_view,
1171                       gboolean starting,
1172                       gpointer user_data)
1173 {
1174         ModestHeaderWindowPrivate *priv = NULL;
1175
1176         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1177         
1178         /* Remove old timeout */
1179         if (priv->updating_banner_timeout > 0) {
1180                 g_source_remove (priv->updating_banner_timeout);
1181                 priv->updating_banner_timeout = 0;
1182         }
1183
1184         /* Create a new timeout */
1185         if (starting) {
1186                 priv->updating_banner_timeout = 
1187                         g_timeout_add (2000, show_updating_banner, user_data);
1188         } else {
1189                 /* Remove the banner if exists */
1190                 if (priv->updating_banner) {
1191                         gtk_widget_destroy (priv->updating_banner);
1192                         priv->updating_banner = NULL;
1193                 }
1194         }
1195 }
1196
1197 static void
1198 set_delete_edit_mode (GtkButton *button,
1199                       ModestHeaderWindow *self)
1200 {
1201         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_DELETE);
1202 }
1203
1204 static void
1205 set_moveto_edit_mode (GtkButton *button,
1206                     ModestHeaderWindow *self)
1207 {
1208         modest_hildon2_window_set_edit_mode (MODEST_HILDON2_WINDOW (self), EDIT_MODE_COMMAND_MOVE);
1209 }
1210
1211 static gboolean 
1212 on_expose_event(GtkTreeView *header_view,
1213                 GdkEventExpose *event,
1214                 gpointer user_data)
1215 {
1216         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
1217         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1218
1219         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1220
1221         if (priv->autoscroll)
1222                 hildon_pannable_area_jump_to (HILDON_PANNABLE_AREA (priv->contents_view), 0.0, 0.0);
1223
1224         return FALSE;
1225 }
1226
1227 static gboolean 
1228 on_map_event(GtkWidget *widget,
1229              GdkEvent *event,
1230              gpointer user_data)
1231 {
1232         ModestHeaderWindow *self = (ModestHeaderWindow *) user_data;
1233         ModestHeaderWindowPrivate *priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1234
1235         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1236
1237         if (priv->progress_hint) {
1238                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
1239         }
1240         return FALSE;
1241 }
1242
1243 static void
1244 on_progress_list_changed (ModestWindowMgr *mgr,
1245                           ModestHeaderWindow *self)
1246 {
1247         update_progress_hint (self);
1248 }
1249
1250 static gboolean
1251 has_active_operations (ModestHeaderWindow *self)
1252 {
1253         GSList *operations = NULL, *node;
1254         ModestMailOperationQueue *queue;
1255         gboolean has_active = FALSE;
1256
1257         queue = modest_runtime_get_mail_operation_queue ();
1258         operations = modest_mail_operation_queue_get_by_source (queue, G_OBJECT (self));
1259
1260         for (node = operations; node != NULL; node = g_slist_next (node)) {
1261                 if (!modest_mail_operation_is_finished (MODEST_MAIL_OPERATION (node->data))) {
1262                         has_active = TRUE;
1263                         break;
1264                 }
1265         }
1266
1267         if (operations) {
1268                 g_slist_foreach (operations, (GFunc) g_object_unref, NULL);
1269                 g_slist_free (operations);
1270         }
1271
1272         return has_active;
1273 }
1274
1275 static void
1276 update_progress_hint (ModestHeaderWindow *self)
1277 {
1278         ModestHeaderWindowPrivate *priv;
1279
1280         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1281
1282         priv->progress_hint = FALSE;
1283
1284         if (has_active_operations (self)) {
1285                 priv->progress_hint = TRUE;
1286         } else {
1287                 priv->progress_hint = FALSE;
1288         }
1289
1290         if (!priv->progress_hint && priv->current_store_account) {
1291                 priv->progress_hint = 
1292                         modest_window_mgr_has_progress_operation_on_account (modest_runtime_get_window_mgr (),
1293                                                                              priv->current_store_account);
1294         }
1295
1296         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1297
1298         if (GTK_WIDGET_VISIBLE (self)) {
1299                 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), priv->progress_hint?1:0);
1300         }
1301 }
1302
1303 gboolean
1304 modest_header_window_toolbar_on_transfer_mode     (ModestHeaderWindow *self)
1305 {
1306         ModestHeaderWindowPrivate *priv= NULL; 
1307
1308         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);
1309         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1310
1311         return priv->progress_hint;
1312 }
1313
1314 gboolean 
1315 modest_header_window_transfer_mode_enabled (ModestHeaderWindow *self)
1316 {
1317         ModestHeaderWindowPrivate *priv;
1318         
1319         g_return_val_if_fail (MODEST_IS_HEADER_WINDOW (self), FALSE);   
1320         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(self);
1321
1322         return priv->progress_hint;
1323 }
1324
1325 static void 
1326 on_mail_operation_started (ModestMailOperation *mail_op,
1327                            gpointer user_data)
1328 {
1329         ModestHeaderWindow *self;
1330         ModestMailOperationTypeOperation op_type;
1331         GObject *source = NULL;
1332
1333         self = MODEST_HEADER_WINDOW (user_data);
1334         op_type = modest_mail_operation_get_type_operation (mail_op);
1335         source = modest_mail_operation_get_source(mail_op);
1336         if (G_OBJECT (self) == source) {
1337                 update_progress_hint (self);
1338         }
1339         g_object_unref (source);
1340 }
1341
1342 static void 
1343 on_mail_operation_finished (ModestMailOperation *mail_op,
1344                             gpointer user_data)
1345 {
1346         ModestHeaderWindow *self;
1347
1348         self = MODEST_HEADER_WINDOW (user_data);
1349
1350         /* Don't disable the progress hint if there are more pending
1351            operations from this window */
1352         update_progress_hint (self);
1353
1354         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
1355 }
1356
1357 static void
1358 on_queue_changed (ModestMailOperationQueue *queue,
1359                   ModestMailOperation *mail_op,
1360                   ModestMailOperationQueueNotification type,
1361                   ModestHeaderWindow *self)
1362 {
1363         ModestHeaderWindowPrivate *priv;
1364
1365         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1366
1367         /* If this operations was created by another window, do nothing */
1368         if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) 
1369                 return;
1370
1371         if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
1372                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1373                                                                G_OBJECT (mail_op),
1374                                                                "operation-started",
1375                                                                G_CALLBACK (on_mail_operation_started),
1376                                                                self);
1377                 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
1378                                                                G_OBJECT (mail_op),
1379                                                                "operation-finished",
1380                                                                G_CALLBACK (on_mail_operation_finished),
1381                                                                self);
1382         } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
1383                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1384                                                                   G_OBJECT (mail_op),
1385                                                                   "operation-started");
1386                 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
1387                                                                   G_OBJECT (mail_op),
1388                                                                   "operation-finished");
1389         }
1390 }
1391
1392 static void
1393 modest_header_window_pack_toolbar (ModestHildon2Window *self,
1394                                    GtkPackType pack_type,
1395                                    GtkWidget *toolbar)
1396 {
1397         ModestHeaderWindowPrivate *priv;
1398
1399         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
1400         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1401
1402         if (pack_type == GTK_PACK_START) {
1403                 gtk_box_pack_start (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
1404         } else {
1405                 gtk_box_pack_end (GTK_BOX (priv->top_vbox), toolbar, FALSE, FALSE, 0);
1406         }
1407 }
1408
1409 static void 
1410 edit_mode_changed (ModestHeaderWindow *header_window,
1411                    gint edit_mode_id,
1412                    gboolean enabled,
1413                    ModestHeaderWindow *self)
1414 {
1415         ModestHeaderWindowPrivate *priv;
1416         ModestHeaderViewFilter filter = MODEST_HEADER_VIEW_FILTER_NONE;
1417
1418         g_return_if_fail (MODEST_IS_HEADER_WINDOW (self));
1419         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1420
1421         switch (edit_mode_id) {
1422         case EDIT_MODE_COMMAND_MOVE:
1423                 filter = MODEST_HEADER_VIEW_FILTER_MOVEABLE;
1424                 break;
1425         case EDIT_MODE_COMMAND_DELETE:
1426                 filter = MODEST_HEADER_VIEW_FILTER_DELETABLE;
1427                 break;
1428         case MODEST_HILDON2_WINDOW_EDIT_MODE_NONE:
1429                 filter = MODEST_HEADER_VIEW_FILTER_NONE;
1430                 break;
1431         }
1432
1433         hildon_tree_view_set_action_area_visible (GTK_TREE_VIEW (priv->header_view), !enabled);
1434         if (enabled) {
1435                 modest_header_view_set_filter (MODEST_HEADER_VIEW (priv->header_view), 
1436                                                filter);
1437         } else {
1438                 GtkTreeSelection *sel;
1439
1440                 /* Unselect all. This will prevent us from keeping a
1441                    reference to a TnyObject that we don't want to
1442                    have */
1443                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->header_view));
1444                 gtk_tree_selection_unselect_all (sel);
1445
1446                 modest_header_view_unset_filter (MODEST_HEADER_VIEW (priv->header_view), 
1447                                                  filter);
1448         }
1449 }
1450
1451 static void 
1452 on_sort_column_changed (GtkTreeSortable *treesortable,
1453                         gpointer         user_data)
1454 {
1455         update_sort_button (MODEST_HEADER_WINDOW (user_data));
1456 }
1457
1458 static void
1459 update_sort_button (ModestHeaderWindow *self)
1460 {
1461         ModestHeaderWindowPrivate *priv;
1462         GtkTreeSortable *sortable;
1463         gint current_sort_colid = -1;
1464         GtkSortType current_sort_type;
1465         const gchar *value = NULL;
1466
1467         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1468
1469         /* This could happen as the first time the model is set the
1470            header_view is still not assigned to priv->header_view */
1471         if (!priv->header_view)
1472                 return;
1473
1474         sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->header_view)));
1475
1476         if (!gtk_tree_sortable_get_sort_column_id (sortable,
1477                                                    &current_sort_colid, &current_sort_type)) {
1478                 value =  _("mcen_li_sort_sender_date_newest");
1479         } else {
1480                 switch (current_sort_colid) {
1481                 case TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN:
1482                 {
1483                         GList *cols = NULL;
1484                         cols = modest_header_view_get_columns (MODEST_HEADER_VIEW (priv->header_view));
1485                         if (cols != NULL) {
1486                                 gpointer flags_sort_type_pointer;
1487                                 flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), 
1488                                                                              MODEST_HEADER_VIEW_FLAG_SORT);
1489                                 if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
1490                                         value = _("mcen_li_sort_priority");
1491                                 else
1492                                         value = _("mcen_li_sort_attachment");
1493                                 g_list_free(cols);      
1494                         }
1495                 } 
1496                 break;
1497                 case TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN:
1498                 case TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN:
1499                         if (current_sort_type == GTK_SORT_ASCENDING)
1500                                 value = _("mcen_li_sort_sender_recipient_az");
1501                         else
1502                                 value = _("mcen_li_sort_sender_recipient_za");
1503                         break;
1504                 case TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN:
1505                 case TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN:
1506                         if (current_sort_type == GTK_SORT_ASCENDING)
1507                                 value = _("mcen_li_sort_date_oldest");
1508                         else
1509                                 value = _("mcen_li_sort_date_newest");
1510                         break;
1511                 case TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN:
1512                         if (current_sort_type == GTK_SORT_ASCENDING)
1513                                 value = _("mcen_li_sort_subject_az");
1514                         else
1515                                 value = _("mcen_li_sort_subject_za");
1516                         break;
1517                 case TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN:
1518                         if (current_sort_type == GTK_SORT_ASCENDING)
1519                                 value = _("mcen_li_sort_size_smallest");
1520                         else
1521                                 value = _("mcen_li_sort_size_largest");
1522                         break;
1523                 } 
1524         }
1525
1526         hildon_button_set_value (HILDON_BUTTON (priv->sort_button), value?value:"");
1527 }
1528
1529 static void
1530 on_horizontal_movement (HildonPannableArea *hildonpannable,
1531                         gint                direction,
1532                         gdouble             initial_x,
1533                         gdouble             initial_y,
1534                         gpointer            user_data)
1535 {
1536         ModestHeaderWindowPrivate *priv;
1537         gint dest_x, dest_y;
1538         TnyHeader *header;
1539
1540         /* Ignore right to left movement */
1541         if (direction == HILDON_MOVEMENT_LEFT)
1542                 return;
1543
1544         /* Get the header to delete */
1545         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (user_data);
1546
1547         /* Get tree view coordinates */
1548         if (!gtk_widget_translate_coordinates ((GtkWidget *) hildonpannable,
1549                                                priv->header_view,
1550                                                initial_x,
1551                                                initial_y,
1552                                                &dest_x,
1553                                                &dest_y))
1554             return;
1555
1556         header = modest_header_view_get_header_at_pos ((ModestHeaderView *) priv->header_view,
1557                                                        dest_x, dest_y);
1558         if (header) {
1559                 delete_header ((GtkWindow *) user_data, header);
1560                 g_object_unref (header);
1561         }
1562 }
1563
1564
1565 static gboolean
1566 on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
1567 {
1568         ModestHeaderWindowPrivate *priv;
1569         HildonPannableArea *pannable;
1570         /* FIXME: set scroll_speed depends on for how long the key was pressed */
1571         gint scroll_speed = 3;
1572
1573         if (event->type == GDK_KEY_RELEASE)
1574                 return FALSE;
1575
1576         priv = MODEST_HEADER_WINDOW_GET_PRIVATE(user_data);
1577
1578         pannable = HILDON_PANNABLE_AREA (priv->contents_view);
1579
1580         switch (event->keyval) {
1581
1582         case GDK_Up:
1583                 priv->autoscroll = FALSE;
1584                 modest_maemo_utils_scroll_pannable(pannable, 0, -scroll_speed);
1585                 break;
1586
1587         case GDK_Down:
1588                 priv->autoscroll = FALSE;
1589                 modest_maemo_utils_scroll_pannable(pannable, 0, scroll_speed);
1590                 break;
1591         }
1592
1593         return FALSE;
1594 }
1595
1596 static void
1597 modest_header_window_show_more (GtkAction *action, ModestWindow *win)
1598 {
1599         ModestHeaderWindow *self;
1600         ModestHeaderWindowPrivate *priv = NULL;
1601
1602         self = MODEST_HEADER_WINDOW (win);
1603         priv = MODEST_HEADER_WINDOW_GET_PRIVATE (self);
1604         if (!priv->header_view)
1605                 return;
1606         
1607         if (modest_header_view_get_not_latest (MODEST_HEADER_VIEW (priv->header_view)) > 0) {
1608                 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (priv->header_view),
1609                                                     modest_header_view_get_show_latest (MODEST_HEADER_VIEW (priv->header_view)) + 
1610                                                     SHOW_LATEST_SIZE);
1611                 update_view (self, NULL);
1612         }
1613 }