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