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