2007-05-30 Murray Cumming <murrayc@murrayc.com>
[modest] / src / maemo / modest-main-window.c
1 /* Copyright (c) 2006, 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 <glib/gi18n.h>
31 #include <gtk/gtktreeviewcolumn.h>
32 #include <tny-account-store-view.h>
33 #include <tny-simple-list.h>
34 #include <tny-maemo-conic-device.h>
35 #include "modest-hildon-includes.h"
36 #include "modest-defs.h"
37 #include <string.h>
38
39 #include "widgets/modest-main-window.h"
40 #include "widgets/modest-msg-edit-window.h"
41 #include "widgets/modest-account-view-window.h"
42 #include "modest-runtime.h"
43 #include "modest-account-mgr-helpers.h"
44 #include "modest-platform.h"
45 #include "modest-widget-memory.h"
46 #include "modest-window-priv.h"
47 #include "modest-main-window-ui.h"
48 #include "modest-main-window-ui-dimming.h"
49 #include "modest-account-mgr.h"
50 #include "modest-tny-account.h"
51 #include "modest-conf.h"
52 #include <modest-maemo-utils.h>
53 #include "modest-tny-platform-factory.h"
54 #include "modest-tny-msg.h"
55 #include "modest-mail-operation.h"
56 #include "modest-icon-names.h"
57 #include "modest-progress-bar-widget.h"
58 #include "modest-text-utils.h"
59 #include "modest-ui-dimming-manager.h"
60 #include "maemo/modest-osso-state-saving.h"
61
62 #ifdef MODEST_HILDON_VERSION_0
63 #include <hildon-widgets/hildon-program.h>
64 #else
65 #include <hildon/hildon-program.h>
66 #endif /*MODEST_HILDON_VERSION_0*/
67
68 #define MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS "ModestMainWindowActionAdditions"
69
70 /* 'private'/'protected' functions */
71 static void modest_main_window_class_init    (ModestMainWindowClass *klass);
72 static void modest_main_window_init          (ModestMainWindow *obj);
73 static void modest_main_window_finalize      (GObject *obj);
74 static gboolean modest_main_window_window_state_event (GtkWidget *widget, 
75                                                            GdkEventWindowState *event, 
76                                                            gpointer userdata);
77
78 static void connect_signals (ModestMainWindow *self);
79
80 static void restore_settings (ModestMainWindow *self);
81 static void save_state (ModestWindow *self);
82
83 static void modest_main_window_show_toolbar   (ModestWindow *window,
84                                                gboolean show_toolbar);
85
86 static void cancel_progressbar (GtkToolButton *toolbutton,
87                                 ModestMainWindow *self);
88
89 static void         on_queue_changed                     (ModestMailOperationQueue *queue,
90                                                           ModestMailOperation *mail_op,
91                                                           ModestMailOperationQueueNotification type,
92                                                           ModestMainWindow *self);
93
94 static void on_account_update                 (TnyAccountStore *account_store, 
95                                                const gchar *account_name,
96                                                gpointer user_data);
97
98 static gboolean on_inner_widgets_key_pressed  (GtkWidget *widget,
99                                                GdkEventKey *event,
100                                                gpointer user_data);
101
102 static void on_configuration_key_changed      (ModestConf* conf, 
103                                                const gchar *key, 
104                                                ModestConfEvent event, 
105                                                ModestMainWindow *self);
106
107 static void set_toolbar_mode                  (ModestMainWindow *self, 
108                                                ModestToolBarModes mode);
109
110 static gboolean set_toolbar_transfer_mode     (ModestMainWindow *self); 
111
112 static void on_show_account_action_activated      (GtkAction *action,
113                                                    gpointer user_data);
114
115 static void on_refresh_account_action_activated   (GtkAction *action,
116                                                    gpointer user_data);
117
118 static void on_send_receive_csm_activated         (GtkMenuItem *item,
119                                                    gpointer user_data);
120 /* list my signals */
121 enum {
122         /* MY_SIGNAL_1, */
123         /* MY_SIGNAL_2, */
124         LAST_SIGNAL
125 };
126
127
128 typedef struct _ModestMainWindowPrivate ModestMainWindowPrivate;
129 struct _ModestMainWindowPrivate {
130         GtkWidget *msg_paned;
131         GtkWidget *main_paned;
132         GtkWidget *main_vbox;
133         GtkWidget *contents_widget;
134
135         /* Progress observers */
136         GtkWidget        *progress_bar;
137         GSList           *progress_widgets;
138
139         /* Tollbar items */
140         GtkWidget   *progress_toolitem;
141         GtkWidget   *cancel_toolitem;
142         GtkWidget   *sort_toolitem;
143         GtkWidget   *refresh_toolitem;
144         ModestToolBarModes current_toolbar_mode;
145
146         /* Merge ids used to add/remove accounts to the ViewMenu*/
147         GByteArray *merge_ids;
148
149         /* On-demand widgets */
150         GtkWidget *accounts_popup;
151         GtkWidget *details_widget;
152
153         /* Optimized view enabled */
154         gboolean optimized_view;
155
156         ModestHeaderView *header_view;
157         ModestFolderView *folder_view;
158
159         ModestMainWindowStyle style;
160         ModestMainWindowContentsStyle contents_style;
161
162         guint progress_bar_timeout;
163
164 };
165 #define MODEST_MAIN_WINDOW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
166                                                 MODEST_TYPE_MAIN_WINDOW, \
167                                                 ModestMainWindowPrivate))
168
169 typedef struct _GetMsgAsyncHelper {
170         ModestMainWindowPrivate *main_window_private;
171         guint action;
172         ModestTnyMsgReplyType reply_type;
173         ModestTnyMsgForwardType forward_type;
174         gchar *from;
175         TnyIterator *iter;
176 } GetMsgAsyncHelper;
177
178
179 /* globals */
180 static GtkWindowClass *parent_class = NULL;
181
182
183 /* Private actions */
184 /* This is the context sensitive menu: */
185 static const GtkActionEntry modest_folder_view_action_entries [] = {
186
187         /* Folder View CSM actions */
188         { "FolderViewCSMNewFolder", NULL, N_("mcen_ti_new_folder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_folder) },
189         { "FolderViewCSMRenameFolder", NULL, N_("mcen_me_user_renamefolder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_rename_folder) },
190         { "FolderViewCSMPasteMsgs", NULL, N_("mcen_me_inbox_paste"), NULL, NULL, NULL },
191         { "FolderViewCSMDeleteFolder", NULL, N_("mcen_me_inbox_delete"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_folder) },
192         { "FolderViewCSMSearchMessages", NULL, N_("mcen_me_inbox_search"), NULL, NULL, NULL },
193         { "FolderViewCSMHelp", NULL, N_("mcen_me_inbox_help"), NULL, NULL, NULL },
194 };
195
196
197 static const GtkToggleActionEntry modest_main_window_toggle_action_entries [] = {
198         { "ToolbarToggleView", MODEST_STOCK_SPLIT_VIEW, N_("gqn_toolb_rss_fldonoff"), "<CTRL>t", NULL, G_CALLBACK (modest_ui_actions_toggle_folders_view), FALSE },
199 };
200
201 /************************************************************************/
202
203 GType
204 modest_main_window_get_type (void)
205 {
206         static GType my_type = 0;
207         if (!my_type) {
208                 static const GTypeInfo my_info = {
209                         sizeof(ModestMainWindowClass),
210                         NULL,           /* base init */
211                         NULL,           /* base finalize */
212                         (GClassInitFunc) modest_main_window_class_init,
213                         NULL,           /* class finalize */
214                         NULL,           /* class data */
215                         sizeof(ModestMainWindow),
216                         1,              /* n_preallocs */
217                         (GInstanceInitFunc) modest_main_window_init,
218                         NULL
219                 };
220                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
221                                                   "ModestMainWindow",
222                                                   &my_info, 0);
223         }
224         return my_type;
225 }
226
227 static void
228 modest_main_window_class_init (ModestMainWindowClass *klass)
229 {
230         GObjectClass *gobject_class;
231         gobject_class = (GObjectClass*) klass;
232         ModestWindowClass *modest_window_class = (ModestWindowClass *) klass;
233
234         parent_class            = g_type_class_peek_parent (klass);
235         gobject_class->finalize = modest_main_window_finalize;
236
237         g_type_class_add_private (gobject_class, sizeof(ModestMainWindowPrivate));
238         
239         modest_window_class->show_toolbar_func = modest_main_window_show_toolbar;
240         modest_window_class->save_state_func = save_state;
241 }
242
243 static void
244 modest_main_window_init (ModestMainWindow *obj)
245 {
246         ModestMainWindowPrivate *priv;
247
248         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(obj);
249
250         priv->msg_paned    = NULL;
251         priv->main_paned   = NULL;      
252         priv->main_vbox    = NULL;
253         priv->header_view  = NULL;
254         priv->folder_view  = NULL;
255         priv->contents_widget  = NULL;
256         priv->accounts_popup  = NULL;
257         priv->details_widget  = NULL;
258
259         priv->progress_widgets  = NULL;
260         priv->progress_bar = NULL;
261         priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
262
263         priv->style  = MODEST_MAIN_WINDOW_STYLE_SPLIT;
264         priv->contents_style  = MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS;
265
266         priv->merge_ids = NULL;
267
268         priv->optimized_view  = FALSE;
269         priv->progress_bar_timeout = 0;
270 }
271
272 static void
273 modest_main_window_finalize (GObject *obj)
274 {
275         ModestMainWindowPrivate *priv;
276
277         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(obj);
278
279         g_slist_free (priv->progress_widgets);
280
281         g_byte_array_free (priv->merge_ids, TRUE);
282
283         if (priv->progress_bar_timeout > 0) {
284                 g_source_remove (priv->progress_bar_timeout);
285                 priv->progress_bar_timeout = 0;
286         }
287
288         G_OBJECT_CLASS(parent_class)->finalize (obj);
289 }
290
291 GtkWidget*
292 modest_main_window_get_child_widget (ModestMainWindow *self,
293                                      ModestWidgetType widget_type)
294 {
295         ModestMainWindowPrivate *priv;
296         GtkWidget *widget;
297         
298         g_return_val_if_fail (self, NULL);
299         g_return_val_if_fail (widget_type >= 0 && widget_type < MODEST_WIDGET_TYPE_NUM,
300                               NULL);
301         
302         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
303
304         switch (widget_type) {
305         case MODEST_WIDGET_TYPE_HEADER_VIEW:
306                 widget = (GtkWidget*)priv->header_view; break;
307         case MODEST_WIDGET_TYPE_FOLDER_VIEW:
308                 widget = (GtkWidget*)priv->folder_view; break;
309         default:
310                 return NULL;
311         }
312
313         return widget ? GTK_WIDGET(widget) : NULL;
314 }
315
316
317
318 static void
319 restore_settings (ModestMainWindow *self)
320 {
321         ModestConf *conf;
322         ModestMainWindowPrivate *priv;
323
324         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
325
326         conf = modest_runtime_get_conf ();
327         
328         modest_widget_memory_restore (conf, G_OBJECT(self),
329                                       MODEST_CONF_MAIN_WINDOW_KEY);
330         modest_widget_memory_restore (conf, G_OBJECT(priv->header_view),
331                                       MODEST_CONF_HEADER_VIEW_KEY);
332         modest_widget_memory_restore (conf, G_OBJECT(priv->main_paned),
333                                       MODEST_CONF_MAIN_PANED_KEY);
334         modest_widget_memory_restore (conf, G_OBJECT(priv->folder_view),
335                                       MODEST_CONF_FOLDER_VIEW_KEY);
336 }
337
338
339 static void
340 save_state (ModestWindow *window)
341 {
342         ModestConf *conf;
343         ModestMainWindow* self = MODEST_MAIN_WINDOW(window);
344         ModestMainWindowPrivate *priv;
345                 
346         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
347         conf = modest_runtime_get_conf ();
348         
349         modest_widget_memory_save (conf,G_OBJECT(self), 
350                                    MODEST_CONF_MAIN_WINDOW_KEY);
351         modest_widget_memory_save (conf, G_OBJECT(priv->main_paned), 
352                                    MODEST_CONF_MAIN_PANED_KEY);
353         modest_widget_memory_save (conf, G_OBJECT(priv->header_view), 
354                                    MODEST_CONF_HEADER_VIEW_KEY);
355         modest_widget_memory_save (conf, G_OBJECT(priv->folder_view), 
356                                    MODEST_CONF_FOLDER_VIEW_KEY);
357 }
358
359 static void
360 wrap_in_scrolled_window (GtkWidget *win, GtkWidget *widget)
361 {
362         if (!gtk_widget_set_scroll_adjustments (widget, NULL, NULL))
363                 gtk_scrolled_window_add_with_viewport
364                         (GTK_SCROLLED_WINDOW(win), widget);
365         else
366                 gtk_container_add (GTK_CONTAINER(win),
367                                    widget);
368 }
369
370
371 static gboolean
372 on_delete_event (GtkWidget *widget, GdkEvent  *event, ModestMainWindow *self)
373 {
374         modest_window_save_state (MODEST_WINDOW(self));
375         return FALSE;
376 }
377
378
379 static void
380 on_account_store_connecting_finished (TnyAccountStore *store, ModestMainWindow *self)
381 {
382         /* When going online, do the equivalent of pressing the send/receive button, 
383          * as per the specification:
384          * (without the check for >0 accounts, though that is not specified): */
385
386         TnyDevice *device = tny_account_store_get_device (store);
387         const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device));
388         printf ("DEBUG: %s: connection id=%s\n", __FUNCTION__, iap_id);
389         
390         /* Stop the existing send queues: */
391         modest_runtime_remove_all_send_queues ();
392         
393         /* Create the send queues again, using the appropriate transport accounts 
394          * for this new connection.
395          * This could be the first time that they are created if this is the first 
396          * connection. */
397         /* TODO: Does this really destroy the TnySendQueues and their threads
398          * We do not want 2 TnySendQueues to exist with the same underlying 
399          * outbox directory. */
400         GSList *account_names = modest_account_mgr_account_names (
401                 modest_runtime_get_account_mgr(), 
402                 TRUE /* enabled accounts only */);
403         GSList *iter = account_names;
404         while (iter) {
405                 const gchar *account_name = (const gchar*)(iter->data);
406                         if (account_name) {
407                         TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (
408                                 modest_tny_account_store_get_transport_account_for_open_connection
409                                                  (modest_runtime_get_account_store(), account_name));
410                         if (account) {
411                                 printf ("debug: %s:\n  Transport account for %s: %s\n", __FUNCTION__, account_name, 
412                                         tny_account_get_id(TNY_ACCOUNT(account)));
413                                 modest_runtime_get_send_queue (account);
414                         }
415                 }
416                 
417                 iter = g_slist_next (iter);
418         }
419         
420         g_slist_free (account_names);
421         
422         
423         modest_ui_actions_do_send_receive (NULL, MODEST_WINDOW (self));
424 }
425
426 static void
427 _folder_view_csm_menu_activated (GtkWidget *widget, gpointer user_data)
428 {
429         g_return_if_fail (MODEST_IS_MAIN_WINDOW (user_data));
430
431         /* Update dimmed */     
432         modest_window_check_dimming_rules (MODEST_WINDOW (user_data));  
433 }
434
435
436 static void
437 connect_signals (ModestMainWindow *self)
438 {       
439         ModestWindowPrivate *parent_priv;
440         ModestMainWindowPrivate *priv;
441         GtkWidget *menu;
442         
443         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
444         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
445         
446         /* folder view */
447         g_signal_connect (G_OBJECT(priv->folder_view), "key-press-event",
448                           G_CALLBACK(on_inner_widgets_key_pressed), self);
449         g_signal_connect (G_OBJECT(priv->folder_view), "folder_selection_changed",
450                           G_CALLBACK(modest_ui_actions_on_folder_selection_changed), self);
451         g_signal_connect (G_OBJECT(priv->folder_view), "folder-display-name-changed",
452                           G_CALLBACK(modest_ui_actions_on_folder_display_name_changed), self);
453
454         menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/FolderViewCSM");
455         gtk_widget_tap_and_hold_setup (GTK_WIDGET (priv->folder_view), menu, NULL, 0);
456         g_signal_connect (G_OBJECT(priv->folder_view), "tap-and-hold",
457                           G_CALLBACK(_folder_view_csm_menu_activated),
458                           self);
459         /* header view */
460 /*      g_signal_connect (G_OBJECT(priv->header_view), "status_update", */
461 /*                        G_CALLBACK(modest_ui_actions_on_header_status_update), self); */
462         g_signal_connect (G_OBJECT(priv->header_view), "header_selected",
463                           G_CALLBACK(modest_ui_actions_on_header_selected), self);
464         g_signal_connect (G_OBJECT(priv->header_view), "header_activated",
465                           G_CALLBACK(modest_ui_actions_on_header_activated), self);
466         g_signal_connect (G_OBJECT(priv->header_view), "item_not_found",
467                           G_CALLBACK(modest_ui_actions_on_item_not_found), self);
468         g_signal_connect (G_OBJECT(priv->header_view), "key-press-event",
469                           G_CALLBACK(on_inner_widgets_key_pressed), self);
470
471         /* window */
472         g_signal_connect (G_OBJECT(self), "delete-event", G_CALLBACK(on_delete_event), self);
473         g_signal_connect (G_OBJECT (self), "window-state-event",
474                           G_CALLBACK (modest_main_window_window_state_event),
475                           NULL);
476         g_signal_connect (G_OBJECT(self), "delete-event", G_CALLBACK(on_delete_event), self);
477
478         /* Mail Operation Queue */
479         g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
480                           "queue-changed",
481                           G_CALLBACK (on_queue_changed),
482                           self);
483
484         /* Track changes in the device name */
485         g_signal_connect (G_OBJECT(modest_runtime_get_conf ()),
486                           "key_changed",
487                           G_CALLBACK (on_configuration_key_changed), 
488                           self);
489
490         /* Track account changes. We need to refresh the toolbar */
491         g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
492                           "account_update",
493                           G_CALLBACK (on_account_update),
494                           self);
495
496         /* Account store */
497         g_signal_connect (G_OBJECT (modest_runtime_get_account_store()), "password_requested",
498                           G_CALLBACK (modest_ui_actions_on_password_requested), self);
499                           
500         /* Device */
501         g_signal_connect (G_OBJECT(modest_runtime_get_account_store()), "connecting-finished",
502                           G_CALLBACK(on_account_store_connecting_finished), self);
503 }
504
505 #if 0
506 /** Idle handler, to send/receive at startup .*/
507 gboolean
508 sync_accounts_cb (ModestMainWindow *win)
509 {
510         modest_ui_actions_do_send_receive (NULL, MODEST_WINDOW (win));
511         return FALSE; /* Do not call this idle handler again. */
512 }
513 #endif
514
515 static void on_hildon_program_is_topmost_notify(GObject *self,
516         GParamSpec *propert_param, gpointer user_data)
517 {
518         HildonProgram *app = HILDON_PROGRAM (self);
519         
520         /*
521         ModestWindow* self = MODEST_WINDOW(user_data);
522         */
523         
524         /* Note that use of hildon_program_set_can_hibernate() 
525          * is generally referred to as "setting the killable flag", 
526          * though hibernation does not seem equal to death.
527          * murrayc */
528                  
529         if (hildon_program_get_is_topmost (app)) {
530                 /* Prevent hibernation when the progam comes to the foreground,
531                  * because hibernation should only happen when the application 
532                  * is in the background: */
533                 hildon_program_set_can_hibernate (app, FALSE);
534         } else {
535                 /* Allow hibernation if the program has gone to the background: */
536                 
537                 /* However, prevent hibernation while the settings are being changed: */
538                 const gboolean hibernation_prevented = 
539                         modest_window_mgr_get_hibernation_is_prevented (
540         modest_runtime_get_window_mgr ()); 
541         
542                 if (hibernation_prevented)
543                         hildon_program_set_can_hibernate (app, FALSE);
544                 else {
545                         /* Allow hibernation, after saving the state: */
546                         modest_osso_save_state();
547                         hildon_program_set_can_hibernate (app, TRUE);
548                 }
549         }
550         
551 }
552
553
554
555 ModestWindow*
556 modest_main_window_new (void)
557 {
558         ModestMainWindow *self = NULL;  
559         ModestMainWindowPrivate *priv = NULL;
560         ModestWindowPrivate *parent_priv = NULL;
561         GtkWidget *folder_win = NULL;
562         ModestDimmingRulesGroup *rules_group = NULL;
563         GtkActionGroup *action_group = NULL;
564         GError *error = NULL;
565         TnyFolderStoreQuery *query = NULL;
566         GdkPixbuf *window_icon = NULL; 
567         ModestConf *conf = NULL;
568         GtkAction *action = NULL;
569
570         self  = MODEST_MAIN_WINDOW(g_object_new(MODEST_TYPE_MAIN_WINDOW, NULL));
571         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
572         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
573
574         parent_priv->ui_manager = gtk_ui_manager_new();
575         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
576
577         action_group = gtk_action_group_new ("ModestMainWindowActions");
578         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
579
580         rules_group = modest_dimming_rules_group_new ("ModestCommonDimmingRules");
581
582         /* Add common actions */
583         gtk_action_group_add_actions (action_group,
584                                       modest_action_entries,
585                                       G_N_ELEMENTS (modest_action_entries),
586                                       self);
587
588         gtk_action_group_add_actions (action_group,
589                                       modest_folder_view_action_entries,
590                                       G_N_ELEMENTS (modest_folder_view_action_entries),
591                                       self);
592
593         gtk_action_group_add_toggle_actions (action_group,
594                                              modest_toggle_action_entries,
595                                              G_N_ELEMENTS (modest_toggle_action_entries),
596                                              self);
597
598         gtk_action_group_add_toggle_actions (action_group,
599                                              modest_main_window_toggle_action_entries,
600                                              G_N_ELEMENTS (modest_main_window_toggle_action_entries),
601                                              self);
602
603         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
604         g_object_unref (action_group);
605
606         /* Load the UI definition */
607         gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager,
608                                          MODEST_UIDIR "modest-main-window-ui.xml", &error);
609         if (error != NULL) {
610                 g_warning ("Could not merge modest-ui.xml: %s", error->message);
611                 g_error_free (error);
612                 error = NULL;
613         }
614
615         /* Add common dimming rules */
616         modest_dimming_rules_group_add_rules (rules_group, 
617                                               modest_dimming_entries,
618                                               G_N_ELEMENTS (modest_dimming_entries),
619                                               self);
620
621         /* Insert dimming rules group for this window */
622         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, rules_group);                                                     
623         g_object_unref (rules_group);
624         
625         /* Add accelerators */
626         gtk_window_add_accel_group (GTK_WINDOW (self), 
627                                     gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
628
629         /* Menubar. Update the state of some toggles */
630         parent_priv->menubar = modest_maemo_utils_menubar_to_menu (parent_priv->ui_manager);
631         conf = modest_runtime_get_conf ();
632         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
633                                             "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarNormalScreenMenu");
634         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
635                                       modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL));
636         action = gtk_ui_manager_get_action (parent_priv->ui_manager, 
637                                             "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarFullScreenMenu");
638         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
639                                       modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL));
640         hildon_window_set_menu (HILDON_WINDOW (self), GTK_MENU (parent_priv->menubar));
641
642         /* Get device name */
643         modest_maemo_utils_get_device_name ();
644
645         /* folder view */
646         query = tny_folder_store_query_new ();
647         tny_folder_store_query_add_item (query, NULL,
648                                          TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
649         priv->folder_view = MODEST_FOLDER_VIEW(modest_folder_view_new (query));
650         if (!priv->folder_view)
651                 g_printerr ("modest: cannot instantiate folder view\n");
652         g_object_unref (G_OBJECT (query));
653         modest_folder_view_set_style (priv->folder_view,
654                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
655
656         /* header view */
657         priv->header_view  =
658                 MODEST_HEADER_VIEW(modest_header_view_new (NULL, MODEST_HEADER_VIEW_STYLE_DETAILS));
659         if (!priv->header_view)
660                 g_printerr ("modest: cannot instantiate header view\n");
661         modest_header_view_set_style (priv->header_view, MODEST_HEADER_VIEW_STYLE_TWOLINES);
662         
663         /* Create scrolled windows */
664         folder_win = gtk_scrolled_window_new (NULL, NULL);
665         priv->contents_widget = gtk_scrolled_window_new (NULL, NULL);
666         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (folder_win),
667                                         GTK_POLICY_NEVER,
668                                         GTK_POLICY_AUTOMATIC);
669         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->contents_widget),
670                                         GTK_POLICY_NEVER,
671                                         GTK_POLICY_AUTOMATIC);
672
673         wrap_in_scrolled_window (folder_win, GTK_WIDGET(priv->folder_view));
674         wrap_in_scrolled_window (priv->contents_widget, GTK_WIDGET(priv->header_view));
675
676         /* paned */
677         priv->main_paned = gtk_hpaned_new ();
678         gtk_paned_add1 (GTK_PANED(priv->main_paned), folder_win);
679         gtk_paned_add2 (GTK_PANED(priv->main_paned), priv->contents_widget);
680         gtk_widget_show (GTK_WIDGET(priv->header_view));
681         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(priv->header_view));
682
683         /* putting it all together... */
684         priv->main_vbox = gtk_vbox_new (FALSE, 6);
685         gtk_box_pack_start (GTK_BOX(priv->main_vbox), priv->main_paned, TRUE, TRUE,0);
686
687         gtk_container_add (GTK_CONTAINER(self), priv->main_vbox);
688         restore_settings (MODEST_MAIN_WINDOW(self));
689
690         /* Set window icon */
691         window_icon = modest_platform_get_icon (MODEST_APP_ICON);
692         gtk_window_set_icon (GTK_WINDOW (self), window_icon);
693         
694         /* Connect signals */
695         connect_signals (self);
696
697         /* Set account store */
698         tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (priv->folder_view),
699                                                   TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()));
700
701         /* Do send & receive when we are idle */
702         /* TODO: Enable this again. I have commented it out because, 
703          * at least in scratchbox, this can cause us to start a second 
704          * update (in response to a connection change) when we are already 
705          * doing an update (started here, at startup). Tinymail doesn't like that.
706          * murrayc.
707          */
708         /* g_idle_add ((GSourceFunc)sync_accounts_cb, self); */
709         
710         HildonProgram *app = hildon_program_get_instance ();
711         hildon_program_add_window (app, HILDON_WINDOW (self));
712         
713         /* Register HildonProgram  signal handlers: */
714         /* These are apparently deprecated, according to the 
715          * "HildonApp/HildonAppView to HildonProgram/HildonWindow migration guide",
716          * though the API reference does not mention that:
717          *
718         g_signal_connect (G_OBJECT(app), "topmost_status_lose",
719                 G_CALLBACK (on_hildon_program_save_state), self);
720         g_signal_connect (G_OBJECT(app), "topmost_status_acquire",
721                 G_CALLBACK (on_hildon_program_status_acquire), self);
722     */
723         g_signal_connect (G_OBJECT(app), "notify::is-topmost",
724                 G_CALLBACK (on_hildon_program_is_topmost_notify), self);
725                 
726         /* Load previous osso state, for instance if we are being restored from 
727          * hibernation:  */
728         modest_osso_load_state();
729
730         return MODEST_WINDOW(self);
731 }
732
733 gboolean 
734 modest_main_window_close_all (ModestMainWindow *self)
735 {
736         GtkWidget *note;
737         GtkResponseType response;
738
739         /* Create the confirmation dialog MSG-NOT308 */
740         note = hildon_note_new_confirmation_add_buttons (GTK_WINDOW (self),
741                                                          _("emev_nc_close_windows"),
742                                                          _("mcen_bd_yes"), GTK_RESPONSE_YES,
743                                                          _("mcen_bd_no"), GTK_RESPONSE_NO,
744                                                          NULL);
745
746         response = gtk_dialog_run (GTK_DIALOG (note));
747         gtk_widget_destroy (GTK_WIDGET (note));
748
749         if (response == GTK_RESPONSE_YES)
750                 return TRUE;
751         else
752                 return FALSE;
753 }
754
755
756 void 
757 modest_main_window_set_style (ModestMainWindow *self, 
758                               ModestMainWindowStyle style)
759 {
760         ModestMainWindowPrivate *priv;
761         ModestWindowPrivate *parent_priv;
762         GtkAction *action;
763
764         g_return_if_fail (MODEST_IS_MAIN_WINDOW (self));
765
766         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
767         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
768
769         /* no change -> nothing to do */
770         if (priv->style == style)
771                 return;
772
773         /* Get toggle button */
774         action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarToggleView");
775
776         priv->style = style;
777         switch (style) {
778         case MODEST_MAIN_WINDOW_STYLE_SIMPLE:
779                 /* Remove main paned */
780                 g_object_ref (priv->main_paned);
781                 gtk_container_remove (GTK_CONTAINER (priv->main_vbox), priv->main_paned);
782
783                 /* Reparent the contents widget to the main vbox */
784                 gtk_widget_reparent (priv->contents_widget, priv->main_vbox);
785
786                 g_signal_handlers_block_by_func (action, modest_ui_actions_toggle_folders_view, self);
787                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
788                 g_signal_handlers_unblock_by_func (action, modest_ui_actions_toggle_folders_view, self);
789
790                 break;
791         case MODEST_MAIN_WINDOW_STYLE_SPLIT:
792                 /* Remove header view */
793                 g_object_ref (priv->contents_widget);
794                 gtk_container_remove (GTK_CONTAINER (priv->main_vbox), priv->contents_widget);
795
796                 /* Reparent the main paned */
797                 gtk_paned_add2 (GTK_PANED (priv->main_paned), priv->contents_widget);
798                 gtk_container_add (GTK_CONTAINER (priv->main_vbox), priv->main_paned);
799
800                 g_signal_handlers_block_by_func (action, modest_ui_actions_toggle_folders_view, self);
801                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
802                 g_signal_handlers_unblock_by_func (action, modest_ui_actions_toggle_folders_view, self);
803
804                 break;
805         default:
806                 g_return_if_reached ();
807         }
808
809         /* Let header view grab the focus if it's being shown */
810         if (priv->contents_style == MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS)
811                 gtk_widget_grab_focus (GTK_WIDGET (priv->header_view));
812         else 
813                 gtk_widget_grab_focus (GTK_WIDGET (priv->contents_widget));
814
815         /* Show changes */
816         gtk_widget_show_all (GTK_WIDGET (priv->main_vbox));
817 }
818
819 ModestMainWindowStyle
820 modest_main_window_get_style (ModestMainWindow *self)
821 {
822         ModestMainWindowPrivate *priv;
823
824         g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), -1);
825
826         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
827         return priv->style;
828 }
829
830
831
832 static gboolean
833 modest_main_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
834 {
835         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
836                 ModestWindowPrivate *parent_priv;
837                 ModestWindowMgr *mgr;
838                 gboolean is_fullscreen;
839                 GtkAction *fs_toggle_action;
840                 gboolean active;
841                 
842                 mgr = modest_runtime_get_window_mgr ();
843                 
844                 is_fullscreen = modest_window_mgr_get_fullscreen_mode (mgr);
845
846                 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
847                 
848                 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
849                 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
850                 if (is_fullscreen != active) {
851                         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
852                 }
853         }
854
855         return FALSE;
856
857 }
858
859 static void
860 set_homogeneous (GtkWidget *widget,
861                  gpointer data)
862 {
863         gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
864         gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
865 }
866
867 static void 
868 modest_main_window_show_toolbar (ModestWindow *self,
869                                  gboolean show_toolbar)
870 {
871         ModestMainWindowPrivate *priv = NULL;
872         ModestWindowPrivate *parent_priv = NULL;        
873         GtkWidget *reply_button = NULL, *menu = NULL;
874         GtkWidget *placeholder = NULL;
875         gint insert_index;
876
877         g_return_if_fail (MODEST_IS_MAIN_WINDOW (self));
878         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
879         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
880
881         /* Set optimized view status */
882         priv->optimized_view = !show_toolbar;
883
884         if (!parent_priv->toolbar) {
885                 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
886                                                                   "/ToolBar");
887
888                 /* Set homogeneous toolbar */
889                 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), 
890                                        set_homogeneous, NULL);
891         
892                 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
893                 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
894                 priv->refresh_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSendReceive");
895                 priv->sort_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSort");
896                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
897                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
898                 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
899                 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
900
901                 /* Add ProgressBar (Transfer toolbar) */ 
902                 priv->progress_bar = modest_progress_bar_widget_new ();
903                 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
904                 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressBarView");
905                 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
906                 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
907                 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
908                 
909                 /* Connect cancel 'clicked' signal to abort progress mode */
910                 g_signal_connect(priv->cancel_toolitem, "clicked",
911                                  G_CALLBACK(cancel_progressbar),
912                                  self);
913                 
914                 /* Add it to the observers list */
915                 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
916
917                 /* Add to window */
918                 hildon_window_add_toolbar (HILDON_WINDOW (self), 
919                                            GTK_TOOLBAR (parent_priv->toolbar));
920
921                 /* Set reply button tap and hold menu */
922                 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, 
923                                                           "/ToolBar/ToolbarMessageReply");
924                 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
925                                                   "/ToolbarReplyCSM");
926                 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
927
928                 /* Set send & receive button tap and hold menu */
929                 on_account_update (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
930                                    NULL, self);
931         }
932
933         if (show_toolbar) {
934                 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ 
935                 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
936                 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
937
938                 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
939                 set_toolbar_mode (MODEST_MAIN_WINDOW(self), TOOLBAR_MODE_NORMAL);
940         } else
941                 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
942
943 }
944
945 static gint
946 compare_display_names (ModestAccountData *a,
947                        ModestAccountData *b)
948 {
949         return strcmp (a->display_name, b->display_name);
950 }
951
952 static void 
953 on_account_update (TnyAccountStore *account_store, 
954                    const gchar *account_name,
955                    gpointer user_data)
956 {
957         GSList *account_names, *iter, *accounts;
958         ModestMainWindow *self;
959         ModestMainWindowPrivate *priv;
960         ModestWindowPrivate *parent_priv;
961         ModestAccountMgr *mgr;
962         gint i, num_accounts;                                   
963         GtkActionGroup *action_group;
964         GList *groups;
965         gchar *default_account;
966         GtkWidget *send_receive_button, *item;
967                 
968         self = MODEST_MAIN_WINDOW (user_data);
969         priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self);
970         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
971
972         /* Get enabled account IDs */
973         mgr = modest_runtime_get_account_mgr ();
974         account_names = modest_account_mgr_account_names (mgr, TRUE);
975         iter = account_names;
976         accounts = NULL;
977
978         while (iter) {
979                 ModestAccountData *account_data = 
980                         modest_account_mgr_get_account_data (mgr, (gchar*) iter->data);
981                 accounts = g_slist_prepend (accounts, account_data);
982
983                 iter = iter->next;
984         }
985         g_slist_free (account_names);
986
987         /* Order the list of accounts by its display name */
988         accounts = g_slist_sort (accounts, (GCompareFunc) compare_display_names);
989         num_accounts = g_slist_length (accounts);
990
991         /* Delete old send&receive popup items. We can not just do a
992            menu_detach because it does not work well with
993            tap_and_hold */
994         if (priv->accounts_popup)
995                 gtk_container_foreach (GTK_CONTAINER (priv->accounts_popup), 
996                                        (GtkCallback) gtk_widget_destroy, NULL);
997
998         /* Delete old entries in the View menu. Do not free groups, it
999            belongs to Gtk+ */
1000         groups = gtk_ui_manager_get_action_groups (parent_priv->ui_manager);
1001         while (groups) {
1002                 if (!strcmp (MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS,
1003                              gtk_action_group_get_name (GTK_ACTION_GROUP (groups->data)))) {
1004                         gtk_ui_manager_remove_action_group (parent_priv->ui_manager, 
1005                                                             GTK_ACTION_GROUP (groups->data));
1006                         groups = NULL;
1007                         /* Remove uis */
1008                         if (priv->merge_ids) {
1009                                 for (i = 0; i < priv->merge_ids->len; i++)
1010                                         gtk_ui_manager_remove_ui (parent_priv->ui_manager, priv->merge_ids->data[i]);
1011                                 g_byte_array_free (priv->merge_ids, TRUE);
1012                         }
1013                         /* We need to call this in order to ensure
1014                            that the new actions are added in the right
1015                            order (alphabetical */
1016                         gtk_ui_manager_ensure_update (parent_priv->ui_manager);
1017                 } else 
1018                         groups = g_list_next (groups);
1019         }
1020         priv->merge_ids = g_byte_array_sized_new (num_accounts);
1021
1022         /* Get send receive button */
1023         send_receive_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1024                                                           "/ToolBar/ToolbarSendReceive");
1025
1026         /* Create the menu */
1027         if (num_accounts > 1) {
1028                 if (!priv->accounts_popup)
1029                         priv->accounts_popup = gtk_menu_new ();
1030                 item = gtk_menu_item_new_with_label (_("mcen_me_toolbar_sendreceive_all"));
1031                 gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_popup), GTK_WIDGET (item));
1032                 g_signal_connect (G_OBJECT (item), 
1033                                   "activate", 
1034                                   G_CALLBACK (on_send_receive_csm_activated),
1035                                   NULL);
1036                 item = gtk_separator_menu_item_new ();
1037                 gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_popup), GTK_WIDGET (item));
1038         }
1039
1040         /* Create a new action group */
1041         default_account = modest_account_mgr_get_default_account (mgr);
1042         action_group = gtk_action_group_new (MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS);
1043         for (i = 0; i < num_accounts; i++) {
1044                 gchar *display_name = NULL;
1045                 
1046                 ModestAccountData *account_data = (ModestAccountData *) g_slist_nth_data (accounts, i);
1047
1048                 /* Create display name. The default account is shown differently */
1049                 if (default_account && account_data->account_name && 
1050                         !(strcmp (default_account, account_data->account_name) == 0)) {
1051                         display_name = g_strdup_printf (_("mcen_me_toolbar_sendreceive_default"), 
1052                                                         account_data->display_name);
1053                 }
1054                 else {
1055                         display_name = g_strdup_printf (_("mcen_me_toolbar_sendreceive_mailbox_n"), 
1056                                                         account_data->display_name);
1057                 }
1058
1059                 /* Create action and add it to the action group. The
1060                    action name must be the account name, this way we
1061                    could know in the handlers the account to show */
1062                 if(account_data->account_name) {
1063                         gchar* item_name, *refresh_action_name;
1064                         guint8 merge_id;
1065                         GtkAction *view_account_action, *refresh_account_action;
1066
1067                         view_account_action = gtk_action_new (account_data->account_name,
1068                                                               display_name, NULL, NULL);
1069                         gtk_action_group_add_action (action_group, view_account_action);
1070
1071                         /* Add ui from account data. We allow 2^9-1 account
1072                            changes in a single execution because we're
1073                            downcasting the guint to a guint8 in order to use a
1074                            GByteArray, it should be enough */
1075                         item_name = g_strconcat (account_data->account_name, "Menu", NULL);
1076                         merge_id = (guint8) gtk_ui_manager_new_merge_id (parent_priv->ui_manager);
1077                         priv->merge_ids = g_byte_array_append (priv->merge_ids, &merge_id, 1);
1078                         gtk_ui_manager_add_ui (parent_priv->ui_manager,
1079                                                merge_id,
1080                                                "/MenuBar/ViewMenu/ViewMenuAdditions",
1081                                                item_name,
1082                                                account_data->account_name,
1083                                                GTK_UI_MANAGER_MENUITEM,
1084                                                FALSE);
1085         
1086                         /* Connect the action signal "activate" */
1087                         g_signal_connect (G_OBJECT (view_account_action),
1088                                           "activate",
1089                                           G_CALLBACK (on_show_account_action_activated),
1090                                           self);
1091
1092                         /* Create the items for the Tools->Send&Receive submenu */
1093                         refresh_action_name = g_strconcat ("SendReceive", account_data->account_name, NULL);
1094                         refresh_account_action = gtk_action_new ((const gchar*) refresh_action_name, 
1095                                                                  display_name, NULL, NULL);
1096                         gtk_action_group_add_action (action_group, refresh_account_action);
1097
1098                         merge_id = (guint8) gtk_ui_manager_new_merge_id (parent_priv->ui_manager);
1099                         priv->merge_ids = g_byte_array_append (priv->merge_ids, &merge_id, 1);
1100                         gtk_ui_manager_add_ui (parent_priv->ui_manager, 
1101                                                merge_id,
1102                                                "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsMenuAdditions",
1103                                                item_name,
1104                                                refresh_action_name,
1105                                                GTK_UI_MANAGER_MENUITEM,
1106                                                FALSE);
1107                         g_free (refresh_action_name);
1108
1109                         g_signal_connect_data (G_OBJECT (refresh_account_action), 
1110                                                "activate", 
1111                                                G_CALLBACK (on_refresh_account_action_activated), 
1112                                                g_strdup (account_data->account_name),
1113                                                (GClosureNotify) g_free,
1114                                                0);
1115
1116                         /* Create item and add it to the send&receive
1117                            CSM. If there is only one account then
1118                            it'll be no menu */
1119                         if (priv->accounts_popup) {
1120                                 item = gtk_menu_item_new_with_label (display_name);
1121                                 gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_popup), GTK_WIDGET (item));
1122                                 g_signal_connect_data (G_OBJECT (item), 
1123                                                        "activate", 
1124                                                        G_CALLBACK (on_send_receive_csm_activated),
1125                                                        g_strdup (account_data->account_name),
1126                                                        (GClosureNotify) g_free,
1127                                                        0);
1128                         }
1129                         g_free (item_name);
1130                 }
1131
1132                 /* Frees */
1133                 g_free (display_name);
1134                 modest_account_mgr_free_account_data (mgr, account_data);
1135         }
1136         gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 1);
1137
1138         if (priv->accounts_popup) {
1139                 /* Mandatory in order to view the menu contents */
1140                 gtk_widget_show_all (priv->accounts_popup);
1141
1142                 /* Setup tap_and_hold just if was not done before*/
1143                 if (!gtk_menu_get_attach_widget (GTK_MENU (priv->accounts_popup)))
1144                         gtk_widget_tap_and_hold_setup (send_receive_button, priv->accounts_popup, NULL, 0);
1145         }
1146
1147         /* Frees */
1148         g_slist_free (accounts);
1149         g_free (default_account);
1150 }
1151
1152 /* 
1153  * This function manages the key events used to navigate between
1154  * header and folder views (when the window is in split view)
1155  *
1156  * FROM         KEY        ACTION
1157  * -------------------------------------------------
1158  * HeaderView   GDK_Left   Move focus to folder view
1159  * FolderView   GDK_Right  Move focus to header view
1160  *
1161  * There is no need to scroll to selected row, the widgets will be the
1162  * responsibles of doing that (probably managing the focus-in event
1163  */
1164 static gboolean 
1165 on_inner_widgets_key_pressed (GtkWidget *widget,
1166                               GdkEventKey *event,
1167                               gpointer user_data)
1168 {
1169         ModestMainWindowPrivate *priv;
1170
1171         priv = MODEST_MAIN_WINDOW_GET_PRIVATE (user_data);
1172
1173         /* Do nothing if we're in SIMPLE style */
1174         if (priv->style == MODEST_MAIN_WINDOW_STYLE_SIMPLE)
1175                 return FALSE;
1176
1177         if (MODEST_IS_HEADER_VIEW (widget) && event->keyval == GDK_Left)
1178                 gtk_widget_grab_focus (GTK_WIDGET (priv->folder_view));
1179         else if (MODEST_IS_FOLDER_VIEW (widget) && event->keyval == GDK_Right)
1180                 gtk_widget_grab_focus (GTK_WIDGET (priv->header_view));
1181
1182         return FALSE;
1183 }
1184
1185 static void
1186 set_alignment (GtkWidget *widget,
1187                gpointer data)
1188 {
1189         gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
1190         gtk_misc_set_padding (GTK_MISC (widget), 0, 0);
1191 }
1192
1193 static GtkWidget *
1194 create_details_widget (TnyFolderStore *folder_store)
1195 {
1196         GtkWidget *vbox;
1197         gchar *label;
1198
1199         vbox = gtk_vbox_new (FALSE, 0);
1200
1201         /* Account description: */
1202         
1203         if (modest_tny_folder_store_is_virtual_local_folders (folder_store)) {
1204                 /* Local folders: */
1205         
1206                 /* Get device name */
1207                 gchar *device_name = modest_conf_get_string (modest_runtime_get_conf(),
1208                                                       MODEST_CONF_DEVICE_NAME, NULL);
1209    
1210                 label = g_strdup_printf ("%s: %s",
1211                                          _("mcen_fi_localroot_description"),
1212                                          device_name);
1213                 gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1214                 g_free (device_name);
1215                 g_free (label);
1216         } else if (TNY_IS_ACCOUNT (folder_store)) {
1217                 TnyAccount *account = TNY_ACCOUNT(folder_store);
1218                 
1219                 if(!strcmp (tny_account_get_id (account), MODEST_MMC_ACCOUNT_ID)) {
1220                         gtk_box_pack_start (GTK_BOX (vbox), 
1221                                 gtk_label_new (tny_account_get_name (account)), 
1222                                 FALSE, FALSE, 0);
1223                 } else {
1224                         /* Other accounts, such as IMAP and POP: */
1225                         
1226                         GString *proto;
1227         
1228                         /* Put proto in uppercase */
1229                         proto = g_string_new (tny_account_get_proto (account));
1230                         proto = g_string_ascii_up (proto);
1231         
1232                         label = g_strdup_printf ("%s %s: %s", 
1233                                                  proto->str,
1234                                                  _("mcen_fi_remoteroot_account"),
1235                                                  tny_account_get_name (account));
1236                         gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1237                         g_string_free (proto, TRUE);
1238                         g_free (label);
1239                 }
1240         }
1241
1242         /* Message count */
1243         
1244         label = g_strdup_printf ("%s: %d", _("mcen_fi_rootfolder_messages"), 
1245                                  modest_tny_folder_store_get_message_count (folder_store));
1246         gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1247         g_free (label);
1248
1249         /* Folder count */
1250         label = g_strdup_printf ("%s: %d", _("mcen_fi_rootfolder_folders"), 
1251                                  modest_tny_folder_store_get_folder_count (folder_store));
1252         gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1253         g_free (label);
1254
1255         /* Size / Date */
1256         if (modest_tny_folder_store_is_virtual_local_folders (folder_store)) {
1257                 /* FIXME: format size */
1258                 label = g_strdup_printf ("%s: %d", _("mcen_fi_rootfolder_size"), 
1259                                          modest_tny_folder_store_get_local_size (folder_store));
1260                 gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1261                 g_free (label);
1262         } else if (TNY_IS_ACCOUNT(folder_store)) {
1263                 TnyAccount *account = TNY_ACCOUNT(folder_store);
1264                 
1265                 if (!strcmp (tny_account_get_id (account), MODEST_MMC_ACCOUNT_ID)) {
1266                         gtk_box_pack_start (GTK_BOX (vbox), 
1267                                 gtk_label_new (tny_account_get_name (account)),
1268                                 FALSE, FALSE, 0);
1269                 } else {
1270                         time_t last_updated;
1271                         gchar *last_updated_string;
1272                         /* Get last updated from configuration */
1273                         last_updated = modest_account_mgr_get_int (modest_runtime_get_account_mgr (), 
1274                                                                   tny_account_get_id (account), 
1275                                                                   MODEST_ACCOUNT_LAST_UPDATED, 
1276                                                                   TRUE);
1277                         if (last_updated > 0) 
1278                                 last_updated_string = modest_text_utils_get_display_date(last_updated);
1279                         else
1280                                 last_updated_string = g_strdup (_("FIXME: Never"));
1281         
1282                         label = g_strdup_printf ("%s: %s", _("mcen_ti_lastupdated"), last_updated_string);
1283                         gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new (label), FALSE, FALSE, 0);
1284                         g_free (last_updated_string);
1285                         g_free (label);
1286                 }
1287         }
1288
1289         /* Set alignment */
1290         gtk_container_foreach (GTK_CONTAINER (vbox), (GtkCallback) set_alignment, NULL);
1291
1292         return vbox;
1293 }
1294
1295 void 
1296 modest_main_window_set_contents_style (ModestMainWindow *self, 
1297                                        ModestMainWindowContentsStyle style)
1298 {
1299         ModestMainWindowPrivate *priv;
1300
1301         g_return_if_fail (MODEST_IS_MAIN_WINDOW (self));
1302
1303         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1304
1305         /* We allow to set the same content style than the previously
1306            set if there are details, because it could happen when we're
1307            selecting different accounts consecutively */
1308         if ((priv->contents_style == style) &&
1309             (priv->contents_style == MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS))
1310                 return;
1311
1312         /* Remove previous child. Delete it if it was an account
1313            details widget */
1314         GtkWidget *content = gtk_bin_get_child (GTK_BIN (priv->contents_widget));
1315         if (content) {
1316                 if (priv->contents_style != MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS)
1317                         g_object_ref (content);
1318                 gtk_container_remove (GTK_CONTAINER (priv->contents_widget), content);
1319         }
1320
1321         priv->contents_style = style;
1322
1323         switch (priv->contents_style) {
1324         case MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS:
1325                 wrap_in_scrolled_window (priv->contents_widget, GTK_WIDGET (priv->header_view));
1326                 break;
1327         case MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS:
1328         {
1329                 /* TODO: show here account details */
1330                 TnyFolderStore *selected_folderstore = 
1331                         modest_folder_view_get_selected (priv->folder_view);
1332                         
1333                 priv->details_widget = create_details_widget (selected_folderstore);
1334
1335                 wrap_in_scrolled_window (priv->contents_widget, 
1336                                  priv->details_widget);
1337                 break;
1338         }
1339         default:
1340                 g_return_if_reached ();
1341         }
1342
1343         /* Show */
1344         gtk_widget_show_all (priv->contents_widget);
1345 }
1346
1347 static void 
1348 on_configuration_key_changed (ModestConf* conf, 
1349                               const gchar *key, 
1350                               ModestConfEvent event, 
1351                               ModestMainWindow *self)
1352 {
1353         ModestMainWindowPrivate *priv;
1354         TnyAccount *account;
1355
1356         if (!key || strcmp (key, MODEST_CONF_DEVICE_NAME))
1357                 return;
1358
1359         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1360
1361         if (priv->contents_style != MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS)
1362                 return;
1363
1364         account = (TnyAccount *) modest_folder_view_get_selected (priv->folder_view);
1365         if (TNY_IS_ACCOUNT (account) &&
1366             !strcmp (tny_account_get_id (account), MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID)) {
1367                 GList *children;
1368                 GtkLabel *label;
1369                 const gchar *device_name;
1370                 gchar *new_text;
1371                 
1372                 /* Get label */
1373                 children = gtk_container_get_children (GTK_CONTAINER (priv->details_widget));
1374                 label = GTK_LABEL (children->data);
1375                 
1376                 device_name = modest_conf_get_string (modest_runtime_get_conf(),
1377                                                       MODEST_CONF_DEVICE_NAME, NULL);
1378                 
1379                 new_text = g_strdup_printf ("%s: %s",
1380                                             _("mcen_fi_localroot_description"),
1381                                             device_name);
1382                 
1383                 gtk_label_set_text (label, new_text);
1384                 gtk_widget_show (GTK_WIDGET (label));
1385                 
1386                 g_free (new_text);
1387                 g_list_free (children);
1388         }
1389 }
1390
1391 static gboolean
1392 set_toolbar_transfer_mode (ModestMainWindow *self)
1393 {
1394         ModestMainWindowPrivate *priv = NULL;
1395         
1396         g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), FALSE);
1397
1398         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1399
1400         set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
1401         
1402         if (priv->progress_bar_timeout > 0) {
1403                 g_source_remove (priv->progress_bar_timeout);
1404                 priv->progress_bar_timeout = 0;
1405         }
1406
1407         return FALSE;
1408 }
1409
1410 static void 
1411 set_toolbar_mode (ModestMainWindow *self, 
1412                   ModestToolBarModes mode)
1413 {
1414         ModestWindowPrivate *parent_priv = NULL;
1415         ModestMainWindowPrivate *priv = NULL;
1416         GtkAction *sort_action = NULL, *refresh_action = NULL, *cancel_action = NULL;
1417         
1418         g_return_if_fail (MODEST_IS_MAIN_WINDOW (self));
1419
1420         parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1421         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1422
1423         g_return_if_fail (GTK_IS_TOOLBAR(parent_priv->toolbar)); 
1424         
1425         sort_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarSort");
1426         refresh_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarSendReceive");
1427         cancel_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1428
1429         /* Sets current toolbar mode */
1430         priv->current_toolbar_mode = mode;
1431
1432         /* Show and hide toolbar items */
1433         switch (mode) {
1434         case TOOLBAR_MODE_NORMAL:
1435                 if (sort_action) 
1436                         gtk_action_set_visible (sort_action, TRUE);
1437                 if (refresh_action) 
1438                         gtk_action_set_visible (refresh_action, TRUE);
1439                 if (priv->progress_toolitem) {
1440                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1441                         gtk_widget_hide (priv->progress_toolitem);
1442                 }
1443                 if (priv->progress_bar)
1444                         gtk_widget_hide (priv->progress_bar);                   
1445                 
1446                 if (cancel_action)
1447                         gtk_action_set_visible (cancel_action, FALSE);
1448
1449                 /* Hide toolbar if optimized view is enabled */
1450                 if (priv->optimized_view)
1451                         gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
1452                 break;
1453         case TOOLBAR_MODE_TRANSFER:
1454                 if (sort_action)
1455                         gtk_action_set_visible (sort_action, FALSE);
1456                 if (refresh_action)
1457                         gtk_action_set_visible (refresh_action, FALSE);
1458                 if (cancel_action)
1459                         gtk_action_set_visible (cancel_action, TRUE);
1460                 if (priv->progress_toolitem) {
1461                         gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1462                         gtk_widget_show (priv->progress_toolitem);
1463                 }
1464                 if (priv->progress_bar)
1465                         gtk_widget_show (priv->progress_bar);                   
1466
1467                 /* Show toolbar if it's hiden (optimized view ) */
1468                 if (priv->optimized_view)
1469                         gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1470                 break;
1471         default:
1472                 g_return_if_reached ();
1473         }
1474 }
1475
1476 static void
1477 cancel_progressbar (GtkToolButton *toolbutton,
1478                     ModestMainWindow *self)
1479 {
1480         GSList *tmp;
1481         ModestMainWindowPrivate *priv;
1482         
1483         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1484
1485         /* Get operation observers and cancel its current operation */
1486         tmp = priv->progress_widgets;
1487         while (tmp) {
1488                 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
1489                 tmp=g_slist_next(tmp);
1490         }
1491 }
1492
1493 static gboolean
1494 observers_empty (ModestMainWindow *self)
1495 {
1496         GSList *tmp = NULL;
1497         ModestMainWindowPrivate *priv;
1498         gboolean is_empty = TRUE;
1499         guint pending_ops = 0;
1500  
1501         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1502         tmp = priv->progress_widgets;
1503
1504         /* Check all observers */
1505         while (tmp && is_empty)  {
1506                 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
1507                 is_empty = pending_ops == 0;
1508                 
1509                 tmp = g_slist_next(tmp);
1510         }
1511         
1512         return is_empty;
1513 }
1514
1515 static void
1516 on_queue_changed (ModestMailOperationQueue *queue,
1517                   ModestMailOperation *mail_op,
1518                   ModestMailOperationQueueNotification type,
1519                   ModestMainWindow *self)
1520 {
1521         ModestMainWindowPrivate *priv;
1522         ModestMailOperationTypeOperation op_type;
1523         ModestToolBarModes mode;
1524         GSList *tmp;
1525         gboolean mode_changed = FALSE;
1526 /*      ModestMailOperationStatus status; */
1527
1528         g_return_if_fail (MODEST_IS_MAIN_WINDOW (self));
1529         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1530                
1531         /* Get toolbar mode from operation id*/
1532         op_type = modest_mail_operation_get_type_operation (mail_op);
1533         switch (op_type) {
1534         case MODEST_MAIL_OPERATION_TYPE_SEND:
1535         case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
1536                 mode = TOOLBAR_MODE_TRANSFER;
1537                 if (priv->current_toolbar_mode == TOOLBAR_MODE_NORMAL)
1538                         mode_changed = TRUE;
1539                 break;
1540         default:
1541                 mode = TOOLBAR_MODE_NORMAL;
1542                 
1543         }
1544                 
1545                        
1546         /* Add operation observers and change toolbar if neccessary*/
1547         tmp = priv->progress_widgets;
1548         switch (type) {
1549         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
1550                 if (mode == TOOLBAR_MODE_TRANSFER) {
1551                         if (mode_changed)
1552                                 set_toolbar_transfer_mode(self);                    
1553                         while (tmp) {
1554                                 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1555                                                                       mail_op);
1556                                 tmp = g_slist_next (tmp);
1557                         }
1558                 }
1559                 break;
1560         case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
1561                 /* Change toolbar mode */
1562                 if (mode == TOOLBAR_MODE_TRANSFER) {                    
1563                         while (tmp) {
1564                                 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
1565                                                                          mail_op);
1566                                 tmp = g_slist_next (tmp);
1567                         }
1568                         
1569                         /* If no more operations are being observed, NORMAL mode is enabled again */
1570                         if (observers_empty (self)) {
1571                                 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
1572                                 
1573                         }
1574                 }
1575
1576                 break;
1577         }       
1578
1579 }
1580
1581 static void 
1582 on_show_account_action_activated  (GtkAction *action,
1583                                    gpointer user_data)
1584 {
1585         ModestAccountData *acc_data;
1586         ModestMainWindow *self;
1587         ModestMainWindowPrivate *priv;
1588         ModestAccountMgr *mgr;
1589         const gchar *acc_name;
1590
1591         self = MODEST_MAIN_WINDOW (user_data);
1592         priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self);
1593
1594         /* Get account data */
1595         acc_name = gtk_action_get_name (action);
1596         mgr = modest_runtime_get_account_mgr ();
1597         acc_data = modest_account_mgr_get_account_data (mgr, acc_name);
1598
1599         /* Set the new visible & active account */
1600         if (acc_data->store_account) { 
1601                 modest_folder_view_set_account_id_of_visible_server_account (priv->folder_view,
1602                                                                              acc_data->store_account->account_name);
1603                 modest_window_set_active_account (MODEST_WINDOW (self), acc_data->account_name);
1604         }
1605
1606         /* Free */
1607         modest_account_mgr_free_account_data (mgr, acc_data);
1608 }
1609
1610 static void
1611 refresh_account (const gchar *account_name)
1612 {
1613         ModestWindow *win;
1614
1615         win = MODEST_WINDOW (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1616
1617         /* If account_name == NULL, we must update all (option All) */
1618         if (!account_name)
1619                 modest_ui_actions_do_send_receive_all (win);
1620         else
1621                 modest_ui_actions_do_send_receive (account_name, win);
1622 }
1623
1624 static void 
1625 on_refresh_account_action_activated  (GtkAction *action,
1626                                       gpointer user_data)
1627 {
1628         refresh_account ((const gchar*) user_data);
1629 }
1630
1631 static void
1632 on_send_receive_csm_activated (GtkMenuItem *item,
1633                                gpointer user_data)
1634 {
1635         refresh_account ((const gchar*) user_data);
1636 }