Emit dbus signal on changing SEEN flag.
[modest] / src / gtk / modest-gtk-window-mgr.c
1 /* Copyright (c) 2008, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include "modest-window.h"
32 #include "modest-gtk-window-mgr.h"
33 #include "modest-msg-edit-window.h"
34 #include "modest-mailboxes-window.h"
35 #include "modest-header-window.h"
36 #include "modest-window-mgr-priv.h"
37 #include "modest-conf.h"
38 #include "modest-defs.h"
39 #include "modest-signal-mgr.h"
40 #include "modest-runtime.h"
41 #include "modest-platform.h"
42 #include "modest-ui-actions.h"
43 #include "modest-debug.h"
44 #include "modest-tny-folder.h"
45 #include "modest-folder-window.h"
46 #include "modest-accounts-window.h"
47 #include "modest-utils.h"
48 #include "modest-tny-msg.h"
49 #include "modest-tny-account.h"
50 #include <modest-shell.h>
51 #include <tny-merge-folder.h>
52
53 /* 'private'/'protected' functions */
54 static void modest_gtk_window_mgr_class_init (ModestGtkWindowMgrClass *klass);
55 static void modest_gtk_window_mgr_instance_init (ModestGtkWindowMgr *obj);
56 static void modest_gtk_window_mgr_finalize   (GObject *obj);
57
58 static gboolean on_window_destroy        (ModestWindow *window,
59                                           GdkEvent *event,
60                                           ModestGtkWindowMgr *self);
61
62 static gboolean modest_gtk_window_mgr_register_window (ModestWindowMgr *self, 
63                                                        ModestWindow *window,
64                                                        ModestWindow *parent);
65 static void modest_gtk_window_mgr_unregister_window (ModestWindowMgr *self, 
66                                                      ModestWindow *window);
67 static void modest_gtk_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
68                                                        gboolean on);
69 static gboolean modest_gtk_window_mgr_get_fullscreen_mode (ModestWindowMgr *self);
70 static void modest_gtk_window_mgr_show_toolbars (ModestWindowMgr *self,
71                                                  GType window_type,
72                                                  gboolean show_toolbars,
73                                                  gboolean fullscreen);
74 static GtkWindow *modest_gtk_window_mgr_get_modal (ModestWindowMgr *self);
75 static void modest_gtk_window_mgr_set_modal (ModestWindowMgr *self, 
76                                              GtkWindow *window,
77                                              GtkWindow *parent);
78 static gboolean modest_gtk_window_mgr_find_registered_header (ModestWindowMgr *self, 
79                                                               TnyHeader *header,
80                                                               ModestWindow **win);
81 static gboolean modest_gtk_window_mgr_find_registered_message_uid (ModestWindowMgr *self, 
82                                                                    const gchar *msg_uid,
83                                                                    ModestWindow **win);
84 static GList *modest_gtk_window_mgr_get_window_list (ModestWindowMgr *self);
85 static gboolean modest_gtk_window_mgr_close_all_windows (ModestWindowMgr *self);
86 static gboolean modest_gtk_window_mgr_close_all_but_initial (ModestWindowMgr *self);
87 static gboolean shell_has_modals (ModestShell *window);
88 static ModestWindow *modest_gtk_window_mgr_show_initial_window (ModestWindowMgr *self);
89 static ModestWindow *modest_gtk_window_mgr_get_current_top (ModestWindowMgr *self);
90 static gboolean modest_gtk_window_mgr_screen_is_on (ModestWindowMgr *self);
91 static void modest_gtk_window_mgr_create_caches (ModestWindowMgr *self);
92 static void on_account_removed (TnyAccountStore *acc_store, 
93                                 TnyAccount *account,
94                                 gpointer user_data);
95 static ModestWindow *modest_gtk_window_mgr_get_folder_window (ModestWindowMgr *self);
96
97 typedef struct _ModestGtkWindowMgrPrivate ModestGtkWindowMgrPrivate;
98 struct _ModestGtkWindowMgrPrivate {
99         GList        *window_list;
100         GMutex       *queue_lock;
101         GQueue       *modal_windows;
102
103         gboolean     fullscreen_mode;
104
105         GHashTable   *destroy_handlers;
106         GHashTable   *viewer_handlers;
107         GSList       *window_state_uids;
108
109         guint        closing_time;
110
111         GSList       *modal_handler_uids;
112         ModestWindow *current_top;
113
114         gulong        accounts_handler;
115         GtkWidget    *shell;
116
117         gboolean      fullscreen;
118 };
119 #define MODEST_GTK_WINDOW_MGR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
120                                                                                    MODEST_TYPE_GTK_WINDOW_MGR, \
121                                                                                    ModestGtkWindowMgrPrivate))
122 /* globals */
123 static GObjectClass *parent_class = NULL;
124
125 GType
126 modest_gtk_window_mgr_get_type (void)
127 {
128         static GType my_type = 0;
129         if (!my_type) {
130                 static const GTypeInfo my_info = {
131                         sizeof(ModestGtkWindowMgrClass),
132                         NULL,           /* base init */
133                         NULL,           /* base finalize */
134                         (GClassInitFunc) modest_gtk_window_mgr_class_init,
135                         NULL,           /* class finalize */
136                         NULL,           /* class data */
137                         sizeof(ModestGtkWindowMgr),
138                         1,              /* n_preallocs */
139                         (GInstanceInitFunc) modest_gtk_window_mgr_instance_init,
140                         NULL
141                 };
142                 my_type = g_type_register_static (MODEST_TYPE_WINDOW_MGR,
143                                                   "ModestGtkWindowMgr",
144                                                   &my_info, 0);
145         }
146         return my_type;
147 }
148
149 static void
150 modest_gtk_window_mgr_class_init (ModestGtkWindowMgrClass *klass)
151 {
152         GObjectClass *gobject_class;
153         ModestWindowMgrClass *mgr_class;
154
155         gobject_class = (GObjectClass*) klass;
156         mgr_class = (ModestWindowMgrClass *) klass;
157
158         parent_class            = g_type_class_peek_parent (klass);
159         gobject_class->finalize = modest_gtk_window_mgr_finalize;
160         mgr_class->register_window = modest_gtk_window_mgr_register_window;
161         mgr_class->unregister_window = modest_gtk_window_mgr_unregister_window;
162         mgr_class->set_fullscreen_mode = modest_gtk_window_mgr_set_fullscreen_mode;
163         mgr_class->get_fullscreen_mode = modest_gtk_window_mgr_get_fullscreen_mode;
164         mgr_class->show_toolbars = modest_gtk_window_mgr_show_toolbars;
165         mgr_class->get_modal = modest_gtk_window_mgr_get_modal;
166         mgr_class->set_modal = modest_gtk_window_mgr_set_modal;
167         mgr_class->find_registered_header = modest_gtk_window_mgr_find_registered_header;
168         mgr_class->find_registered_message_uid = modest_gtk_window_mgr_find_registered_message_uid;
169         mgr_class->get_window_list = modest_gtk_window_mgr_get_window_list;
170         mgr_class->close_all_windows = modest_gtk_window_mgr_close_all_windows;
171         mgr_class->close_all_but_initial = modest_gtk_window_mgr_close_all_but_initial;
172         mgr_class->show_initial_window = modest_gtk_window_mgr_show_initial_window;
173         mgr_class->get_current_top = modest_gtk_window_mgr_get_current_top;
174         mgr_class->screen_is_on = modest_gtk_window_mgr_screen_is_on;
175         mgr_class->create_caches = modest_gtk_window_mgr_create_caches;
176         mgr_class->get_folder_window = modest_gtk_window_mgr_get_folder_window;
177
178         g_type_class_add_private (gobject_class, sizeof(ModestGtkWindowMgrPrivate));
179
180 }
181
182 static void
183 modest_gtk_window_mgr_instance_init (ModestGtkWindowMgr *obj)
184 {
185         ModestGtkWindowMgrPrivate *priv;
186
187         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(obj);
188         priv->window_list = NULL;
189         priv->fullscreen_mode = FALSE;
190         priv->window_state_uids = NULL;
191
192         priv->modal_windows = g_queue_new ();
193         priv->queue_lock = g_mutex_new ();
194         priv->fullscreen = FALSE;
195
196         /* Could not initialize it from gconf, singletons are not
197            ready yet */
198         priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
199         priv->viewer_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
200
201         priv->closing_time = 0;
202
203         priv->modal_handler_uids = NULL;
204         priv->shell = modest_shell_new ();
205         gtk_widget_show (priv->shell);
206 }
207
208 static void
209 modest_gtk_window_mgr_finalize (GObject *obj)
210 {
211         ModestGtkWindowMgrPrivate *priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(obj);
212         ModestTnyAccountStore *acc_store;
213
214         modest_signal_mgr_disconnect_all_and_destroy (priv->window_state_uids);
215         priv->window_state_uids = NULL;
216
217         acc_store = modest_runtime_get_account_store ();
218         if (acc_store && g_signal_handler_is_connected (acc_store, priv->accounts_handler))
219                 g_signal_handler_disconnect (acc_store, priv->accounts_handler);
220
221         if (priv->window_list) {
222                 GList *iter = priv->window_list;
223                 /* unregister pending windows */
224                 while (iter) {
225                         ModestWindow *window = (ModestWindow *) iter->data;
226                         iter = g_list_next (iter);
227                         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), window);
228                 }
229                 g_list_free (priv->window_list);
230                 priv->window_list = NULL;
231         }
232
233         /* Free the hash table with the handlers */
234         if (priv->destroy_handlers) {
235                 g_hash_table_destroy (priv->destroy_handlers);
236                 priv->destroy_handlers = NULL;
237         }
238
239         if (priv->viewer_handlers) {
240                 g_hash_table_destroy (priv->viewer_handlers);
241                 priv->viewer_handlers = NULL;
242         }
243
244         modest_signal_mgr_disconnect_all_and_destroy (priv->modal_handler_uids);
245         priv->modal_handler_uids = NULL;
246
247         if (priv->modal_windows) {
248                 g_mutex_lock (priv->queue_lock);
249                 g_queue_free (priv->modal_windows);
250                 priv->modal_windows = NULL;
251                 g_mutex_unlock (priv->queue_lock);
252         }
253         g_mutex_free (priv->queue_lock);
254
255         G_OBJECT_CLASS(parent_class)->finalize (obj);
256 }
257
258 ModestWindowMgr*
259 modest_gtk_window_mgr_new (void)
260 {
261         return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_GTK_WINDOW_MGR, NULL));
262 }
263
264 static gboolean
265 modest_gtk_window_mgr_close_all_windows (ModestWindowMgr *self)
266 {
267         ModestGtkWindowMgrPrivate *priv = NULL;
268         gboolean ret_value = FALSE;
269         ModestWindow *window;
270         gboolean failed = FALSE;
271
272         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
273         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
274
275         while ((window = modest_shell_peek_window (MODEST_SHELL (priv->shell))) != NULL) {
276                 ret_value = modest_shell_delete_window (MODEST_SHELL (priv->shell), window);
277                 if (ret_value == TRUE) {
278                         failed = TRUE;
279                         break;
280                 }
281         }
282
283         return !failed;
284 }
285
286 static gint
287 compare_msguids (ModestWindow *win,
288                  const gchar *uid)
289 {
290         const gchar *msg_uid;
291
292         if ((!MODEST_IS_MSG_EDIT_WINDOW (win)) && (!MODEST_IS_MSG_VIEW_WINDOW (win)))
293                 return 1;
294
295         /* Get message uid from msg window */
296         if (MODEST_IS_MSG_EDIT_WINDOW (win)) {
297                 msg_uid = modest_msg_edit_window_get_message_uid (MODEST_MSG_EDIT_WINDOW (win));
298                 if (msg_uid && uid &&!strcmp (msg_uid, uid))
299                         return 0;
300         } else {
301                 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
302         }
303
304         if (msg_uid && uid &&!strcmp (msg_uid, uid))
305                 return 0;
306         else
307                 return 1;
308 }
309
310 static gint
311 compare_headers (ModestWindow *win,
312                  TnyHeader *header)
313 {
314         TnyHeader *my_header;
315         gint result = 1;
316
317         if (!MODEST_IS_MSG_VIEW_WINDOW (win))
318                 return 1;
319
320         /* Get message uid from msg window */
321         my_header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (win));
322         if (my_header) {
323                 if (my_header == header)
324                         result = 0;
325                 g_object_unref (my_header);
326         }
327         return result;
328 }
329
330
331 static gboolean
332 modest_gtk_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
333                                               ModestWindow **win)
334 {
335         ModestGtkWindowMgrPrivate *priv = NULL;
336         gchar* uid = NULL;
337         gboolean has_header, has_window = FALSE;
338         GList *item = NULL;
339
340         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
341         g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
342         
343         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
344
345         has_header = MODEST_WINDOW_MGR_CLASS (parent_class)->find_registered_header (self, header, win);
346         
347         uid = modest_tny_folder_get_header_unique_id (header);
348         
349         item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids);
350         if (item) {
351                 has_window = TRUE;
352                 if (win) {
353                         if ((!MODEST_IS_MSG_VIEW_WINDOW(item->data)) && 
354                             (!MODEST_IS_MSG_EDIT_WINDOW (item->data)))
355                                 g_debug ("not a valid window!");
356                         else {
357                                 g_debug ("found a window");
358                                 *win = MODEST_WINDOW (item->data);
359                         }
360                 }
361         }
362         g_free (uid);
363         
364         return has_header || has_window;
365 }
366
367 static gboolean
368 modest_gtk_window_mgr_find_registered_message_uid (ModestWindowMgr *self, const gchar *msg_uid,
369                                                        ModestWindow **win)
370 {
371         ModestGtkWindowMgrPrivate *priv = NULL;
372         gboolean has_header, has_window = FALSE;
373         GList *item = NULL;
374
375         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
376         g_return_val_if_fail (msg_uid && msg_uid[0] != '\0', FALSE);
377         
378         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
379
380         has_header = MODEST_WINDOW_MGR_CLASS (parent_class)->find_registered_message_uid (self, msg_uid, win);
381         
382         item = g_list_find_custom (priv->window_list, msg_uid, (GCompareFunc) compare_msguids);
383         if (item) {
384                 has_window = TRUE;
385                 if (win) {
386                         if ((!MODEST_IS_MSG_VIEW_WINDOW(item->data)) && 
387                             (!MODEST_IS_MSG_EDIT_WINDOW (item->data)))
388                                 g_debug ("not a valid window!");
389                         else {
390                                 g_debug ("found a window");
391                                 *win = MODEST_WINDOW (item->data);
392                         }
393                 }
394         }
395         
396         return has_header || has_window;
397 }
398
399 static GList *
400 modest_gtk_window_mgr_get_window_list (ModestWindowMgr *self)
401 {
402         ModestGtkWindowMgrPrivate *priv;
403
404         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
405         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
406
407         return g_list_copy (priv->window_list);
408 }
409
410 static gboolean
411 modest_gtk_window_mgr_register_window (ModestWindowMgr *self, 
412                                            ModestWindow *window,
413                                            ModestWindow *parent)
414 {
415         GList *win;
416         ModestGtkWindowMgrPrivate *priv;
417         gint *handler_id;
418         gboolean nested_msg = FALSE;
419         ModestWindow *current_top;
420
421         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
422
423         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
424
425         /* Try to close active modal dialogs */
426         if (modest_window_mgr_get_num_windows (self) &&
427             !_modest_window_mgr_close_active_modals (self))
428                 return FALSE;
429
430         current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
431
432         win = g_list_find (priv->window_list, window);
433         if (win) {
434                 /* this is for the case we want to register the window
435                    and it was already registered */
436                 gtk_window_present (GTK_WINDOW (priv->shell));
437                 return FALSE;
438         }
439
440         /* Do not allow standalone editors or standalone viewers */
441         if (!current_top &&
442             (MODEST_IS_MSG_VIEW_WINDOW (window) ||
443              MODEST_IS_MSG_EDIT_WINDOW (window)))
444                 modest_window_mgr_show_initial_window (self);
445
446         if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
447                 gchar *uid;
448                 TnyHeader *header;
449
450                 uid = g_strdup (modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window)));
451                 
452                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
453
454                 if (uid == NULL)
455                         uid = modest_tny_folder_get_header_unique_id (header);
456                 /* Embedded messages do not have uid */
457                 if (uid) {
458                         if (g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids)) {
459                                 g_debug ("%s found another view window showing the same header", __FUNCTION__);
460                                 g_free (uid);
461                                 g_object_unref (header);
462                                 return FALSE;
463                         }
464                         g_free (uid);
465                 } else if (header) {
466                         if (g_list_find_custom (priv->window_list, header, (GCompareFunc) compare_headers)) {
467                                 g_debug ("%s found another view window showing the same header", __FUNCTION__);
468                                 g_object_unref (header);
469                                 return FALSE;
470                         }
471                 }
472                 if (header)
473                         g_object_unref (header);
474         }
475
476         /* Do not go backwards */
477         if ((MODEST_IS_MSG_VIEW_WINDOW (current_top) ||
478              MODEST_IS_MSG_EDIT_WINDOW (current_top) ||
479              MODEST_IS_HEADER_WINDOW (current_top)) &&
480             (MODEST_IS_FOLDER_WINDOW (window) ||
481              MODEST_IS_ACCOUNTS_WINDOW (window) ||
482              MODEST_IS_MAILBOXES_WINDOW (window))) {
483                 gtk_window_present (GTK_WINDOW (priv->shell));
484                 return FALSE;
485         }
486
487         if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_FOLDER_WINDOW (window)) {
488                 gboolean retval;
489
490                 retval = modest_shell_delete_window (MODEST_SHELL (priv->shell), MODEST_WINDOW (window));
491
492                 if (retval) {
493                         gtk_window_present (GTK_WINDOW (priv->shell));
494                         return FALSE;
495                 }
496                 current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
497         }
498
499         if (MODEST_IS_MAILBOXES_WINDOW (current_top) && MODEST_IS_MAILBOXES_WINDOW (window)) {
500                 gtk_window_present (GTK_WINDOW (priv->shell));
501                 return FALSE;
502         }
503
504         /* Mailboxes window can not replace folder windows */
505         if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_MAILBOXES_WINDOW (window)) {
506                 gtk_window_present (GTK_WINDOW (priv->shell));
507                 return FALSE;
508         }
509
510         /* Trying to open a folders window and a mailboxes window at
511            the same time from the accounts window is not allowed */
512         if (MODEST_IS_MAILBOXES_WINDOW (current_top) &&
513             MODEST_IS_FOLDER_WINDOW (window) &&
514             MODEST_IS_ACCOUNTS_WINDOW (parent)) {
515                 gtk_window_present (GTK_WINDOW (priv->shell));
516                 return FALSE;
517         }
518
519         if (MODEST_IS_HEADER_WINDOW (current_top) && MODEST_IS_HEADER_WINDOW (window)) {
520                 g_debug ("Trying to register a second header window is not allowed");
521                 gtk_window_present (GTK_WINDOW (priv->shell));
522                 return FALSE;
523         }
524
525         if (!MODEST_WINDOW_MGR_CLASS (parent_class)->register_window (self, window, parent))
526                 goto fail;
527
528         /* Add to list. Keep a reference to the window */
529         g_object_ref (window);
530         priv->window_list = g_list_prepend (priv->window_list, window);
531
532         nested_msg = MODEST_IS_MSG_VIEW_WINDOW (window) &&
533                 MODEST_IS_MSG_VIEW_WINDOW (parent);
534
535         /* Close views if they're being shown. Nevertheless we must
536            allow nested messages */
537         if (!nested_msg &&
538             (MODEST_IS_MSG_EDIT_WINDOW (current_top) ||
539              MODEST_IS_MSG_VIEW_WINDOW (current_top))) {
540                 gboolean retval;
541
542                 /* If the current view has modal dialogs then
543                    we fail to register the new view */
544                 if ((current_top != NULL) &&
545                     shell_has_modals (MODEST_SHELL (priv->shell))) {
546                         /* Window on top but it has opened dialogs */
547                         goto fail;
548                 }
549
550                 /* Close the current view */
551                 retval = modest_shell_delete_window (MODEST_SHELL (priv->shell), current_top);
552                 if (retval) {
553                         /* Cancelled closing top window, then we fail to register */
554                         goto fail;
555                 }
556         }
557
558         /* Listen to object destruction */
559         handler_id = g_malloc0 (sizeof (gint));
560         *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
561         g_hash_table_insert (priv->destroy_handlers, window, handler_id);
562
563         /* Show toolbar always */
564         modest_window_show_toolbar (window, TRUE);
565
566         modest_shell_add_window (MODEST_SHELL (priv->shell), window);
567
568         return TRUE;
569 fail:
570         /* Add to list. Keep a reference to the window */
571         priv->window_list = g_list_remove (priv->window_list, window);
572         g_object_unref (window);
573         current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
574         if (current_top)
575                 gtk_window_present (GTK_WINDOW (priv->shell));
576         return FALSE;
577 }
578
579 static void
580 cancel_window_operations (ModestWindow *window)
581 {
582         GSList* pending_ops = NULL;
583
584         /* cancel open and receive operations */
585         pending_ops = modest_mail_operation_queue_get_by_source (modest_runtime_get_mail_operation_queue (), 
586                                                                  G_OBJECT (window));
587         while (pending_ops != NULL) {
588                 ModestMailOperationTypeOperation type;
589                 GSList* tmp_list = NULL;
590
591                 type = modest_mail_operation_get_type_operation (MODEST_MAIL_OPERATION (pending_ops->data));
592                 if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
593                     type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
594                     type == MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE) {
595                         modest_mail_operation_cancel (pending_ops->data);
596                 }
597                 g_object_unref (G_OBJECT (pending_ops->data));
598                 tmp_list = pending_ops;
599                 pending_ops = g_slist_next (pending_ops);
600                 g_slist_free_1 (tmp_list);
601         }
602 }
603
604 static gboolean
605 shell_has_modals (ModestShell *shell)
606 {
607         GList *toplevels;
608         GList *node;
609         gboolean retvalue = FALSE;
610
611         /* First we fetch all toplevels */
612         toplevels = gtk_window_list_toplevels ();
613         for (node = toplevels; node != NULL; node = g_list_next (node)) {
614                 if (GTK_IS_WINDOW (node->data) &&
615                     gtk_window_get_transient_for (GTK_WINDOW (node->data)) == GTK_WINDOW (shell) &&
616                     GTK_WIDGET_VISIBLE (node->data)) {
617                         retvalue = TRUE;
618                         break;
619                 }
620         }
621         g_list_free (toplevels);
622         return retvalue;
623 }
624
625 static gboolean
626 on_window_destroy (ModestWindow *window, 
627                    GdkEvent *event,
628                    ModestGtkWindowMgr *self)
629 {
630         ModestGtkWindowMgrPrivate *priv;
631         gboolean no_propagate = FALSE;
632
633         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
634
635         /* Do not close the window if it has modals on top */
636         if (!MODEST_IS_MSG_EDIT_WINDOW (window) && shell_has_modals (MODEST_SHELL (priv->shell)))
637                 return TRUE;
638
639         if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
640                 gboolean sent = FALSE;
641                 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
642                 /* Save currently edited message to Drafts if it was not sent */
643                 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
644                         ModestMsgEditWindow *edit_window;
645                         MsgData *data;
646
647                         edit_window = MODEST_MSG_EDIT_WINDOW (window);
648                         data = modest_msg_edit_window_get_msg_data (edit_window);
649
650                         if (data) {
651                                 gint parts_count;
652                                 guint64 parts_size, available_size, expected_size;
653
654                                 available_size = modest_utils_get_available_space (NULL);
655                                 modest_msg_edit_window_get_parts_size (edit_window, &parts_count, &parts_size);
656                                 expected_size = modest_tny_msg_estimate_size (data->plain_body,
657                                                                               data->html_body,
658                                                                               parts_count,
659                                                                               parts_size);
660                                 modest_msg_edit_window_free_msg_data (edit_window, data);
661                                 data = NULL;
662
663                                 /* If there is not enough space
664                                    available for saving the message
665                                    then show an error and close the
666                                    window without saving */
667                                 if (expected_size >= available_size) {
668                                         modest_platform_run_information_dialog (GTK_WINDOW (edit_window),
669                                                                                 _("mail_in_ui_save_error"),
670                                                                                 FALSE);
671                                 } else {
672                                         if (!modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window)))
673                                                 return TRUE;
674                                 }
675                         } else {
676                                 g_warning ("Edit window without message data. This is probably a bug");
677                         }
678                 }
679         }
680
681         /* Unregister window */
682         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (self), window);
683         no_propagate = FALSE;
684
685         return no_propagate;
686 }
687
688 static void
689 modest_gtk_window_mgr_unregister_window (ModestWindowMgr *self, 
690                                          ModestWindow *window)
691 {
692         GList *win;
693         ModestGtkWindowMgrPrivate *priv;
694         gulong *tmp, handler_id;
695         guint num_windows;
696
697         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
698         g_return_if_fail (MODEST_IS_WINDOW (window));
699
700         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
701
702         win = g_list_find (priv->window_list, window);
703         if (!win) {
704                 g_debug ("Trying to unregister a window that has not being registered yet");
705                 return;
706         }
707
708         /* Remove the viewer window handler from the hash table. The
709            HashTable could not exist if the main window was closed
710            when there were other windows remaining */
711         if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
712                 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
713                 /* If the viewer was created without a main window
714                    (for example when opening a message through D-Bus
715                    the viewer handlers was not registered */
716                 if (tmp) {
717                         g_signal_handler_disconnect (window, *tmp);
718                         g_hash_table_remove (priv->viewer_handlers, window);
719                 }
720         }
721
722         /* Remove from list & hash table */
723         priv->window_list = g_list_remove_link (priv->window_list, win);
724         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
725         handler_id = *tmp;
726
727         g_hash_table_remove (priv->destroy_handlers, window);
728
729         /* cancel open and receive operations */
730         cancel_window_operations (window);
731
732         /* Disconnect the "delete-event" handler, we won't need it anymore */
733         g_signal_handler_disconnect (window, handler_id);
734
735         /* Destroy the window */
736         g_object_unref (win->data);
737         g_list_free (win);
738
739         MODEST_WINDOW_MGR_CLASS (parent_class)->unregister_window (self, window);
740
741         /* We have to get the number of windows here in order not to
742            emit the signal too many times */
743         num_windows = modest_window_mgr_get_num_windows (self);
744
745         /* If there are no more windows registered emit the signal */
746         if (num_windows == 0)
747                 g_signal_emit_by_name (self, "window-list-empty");
748 }
749
750
751 static void
752 modest_gtk_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
753                                                gboolean on)
754 {
755         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
756
757         ModestGtkWindowMgrPrivate *priv = NULL;
758         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
759         
760         priv->fullscreen = on;
761
762         if (on) {
763                 gtk_window_fullscreen (GTK_WINDOW (priv->shell));
764         } else {
765                 gtk_window_unfullscreen (GTK_WINDOW (priv->shell));
766         }
767         return;
768 }
769
770 static gboolean
771 modest_gtk_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
772 {
773         ModestGtkWindowMgrPrivate *priv = NULL;
774         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
775         
776         return priv->fullscreen;
777 }
778
779 static void 
780 modest_gtk_window_mgr_show_toolbars (ModestWindowMgr *self,
781                                          GType window_type,
782                                          gboolean show_toolbars,
783                                          gboolean fullscreen)
784 {
785         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
786
787         return;
788 }
789
790 static gint
791 look_for_transient (gconstpointer a,
792                     gconstpointer b)
793 {
794         GtkWindow *win, *child;
795
796         if (a == b)
797                 return 1;
798
799         child = (GtkWindow *) b;
800         win = (GtkWindow *) a;
801
802         if ((gtk_window_get_transient_for (win) == child) &&
803             GTK_WIDGET_VISIBLE (win))
804                 return 0;
805         else
806                 return 1;
807 }
808
809 static GtkWindow *
810 modest_gtk_window_mgr_get_modal (ModestWindowMgr *self)
811 {
812         ModestGtkWindowMgrPrivate *priv;
813         GList *toplevel_list;
814         GtkWidget *toplevel;
815
816         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
817         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
818
819         /* Get current top */
820         toplevel = priv->shell;
821         toplevel_list = gtk_window_list_toplevels ();
822
823         while (toplevel) {
824                 GList *parent_link;
825
826                 parent_link = g_list_find_custom (toplevel_list, toplevel, look_for_transient);
827                 if (parent_link)
828                         toplevel = (GtkWidget *) parent_link->data;
829                 else
830                         break;
831         }
832
833         if (toplevel && GTK_WIDGET_VISIBLE (toplevel) && gtk_window_get_modal ((GtkWindow *) toplevel))
834                 return (GtkWindow *) toplevel;
835         else
836                 return NULL;
837 }
838
839
840 static void
841 modest_gtk_window_mgr_set_modal (ModestWindowMgr *self, 
842                                      GtkWindow *window,
843                                      GtkWindow *parent)
844 {
845         ModestGtkWindowMgrPrivate *priv;
846
847         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
848         g_return_if_fail (GTK_IS_WINDOW (window));
849
850         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
851
852         gtk_window_set_modal (window, TRUE);
853
854         if (GTK_IS_WINDOW (parent)) {
855                 gtk_window_set_transient_for (window, parent);
856         } else {
857                 gtk_window_set_transient_for (window, GTK_WINDOW (priv->shell));
858         }
859         gtk_window_set_destroy_with_parent (window, TRUE);
860 }
861
862 static void
863 close_all_but_first (gpointer data)
864 {
865         gint num_windows, i;
866         gboolean retval;
867         ModestGtkWindowMgrPrivate *priv;
868
869         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(data);
870         num_windows = modest_shell_count_windows (MODEST_SHELL (priv->shell));
871
872         for (i = 0; i < (num_windows - 1); i++) {
873                 ModestWindow *current_top;
874
875                 /* Close window */
876                 current_top = modest_shell_peek_window (MODEST_SHELL (priv->shell));
877                 retval = modest_shell_delete_window (MODEST_SHELL (priv->shell), current_top);
878         }
879 }
880
881 static gboolean
882 on_idle_close_all_but_first (gpointer data)
883 {
884         gdk_threads_enter ();
885         close_all_but_first (data);
886         gdk_threads_leave ();
887
888         return FALSE;
889 }
890
891 static void
892 on_account_removed (TnyAccountStore *acc_store,
893                     TnyAccount *account,
894                     gpointer user_data)
895 {
896         ModestWindow *current_top;
897         ModestGtkWindowMgrPrivate *priv;
898
899         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (user_data);
900
901         /* Ignore transport account removals */
902         if (TNY_IS_TRANSPORT_ACCOUNT (account))
903                 return;
904
905         current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
906
907         /* if we're showing the header view of the currently deleted
908            account, or the outbox and we deleted the last account,
909            then close the window */
910         if (current_top &&
911             (MODEST_IS_HEADER_WINDOW (current_top) ||
912              MODEST_IS_FOLDER_WINDOW (current_top))) {
913                     const gchar *acc_name;
914
915                     acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
916
917                     /* We emit it in an idle, because sometimes this
918                        function could called when the account settings
919                        dialog is about to close but still there. That
920                        modal dialog would otherwise, prevent the
921                        windows from being closed */
922                     if (!strcmp (acc_name, modest_window_get_active_account (current_top)))
923                             g_idle_add (on_idle_close_all_but_first, (gpointer) user_data);
924         }
925 }
926
927 static ModestWindow *
928 modest_gtk_window_mgr_show_initial_window (ModestWindowMgr *self)
929 {
930         ModestWindow *initial_window = NULL;
931         ModestGtkWindowMgrPrivate *priv;
932
933         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
934
935         /* Connect to the account store "account-removed" signal". We
936            do this here because in the init the singletons are still
937            not initialized properly */
938         if (!g_signal_handler_is_connected (modest_runtime_get_account_store (),
939                                             priv->accounts_handler)) {
940                 priv->accounts_handler = g_signal_connect (modest_runtime_get_account_store (),
941                                                            "account-removed",
942                                                            G_CALLBACK (on_account_removed),
943                                                            self);
944         }
945
946         /* Return accounts window */
947         initial_window = MODEST_WINDOW (modest_accounts_window_new ());
948         modest_window_mgr_register_window (self, initial_window, NULL);
949
950         return initial_window;
951 }
952
953
954 static ModestWindow *
955 modest_gtk_window_mgr_get_current_top (ModestWindowMgr *self)
956 {
957         ModestGtkWindowMgrPrivate *priv;
958
959         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
960         return (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
961 }
962
963 static gint 
964 find_folder_window (gconstpointer a,
965                     gconstpointer b)
966 {
967         return (MODEST_IS_FOLDER_WINDOW (a)) ? 0 : 1;
968 }
969
970 static ModestWindow *
971 modest_gtk_window_mgr_get_folder_window (ModestWindowMgr *self)
972 {
973         ModestGtkWindowMgrPrivate *priv;
974         GList *window;
975
976         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
977
978         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
979
980         window = g_list_find_custom (priv->window_list,
981                                      NULL,
982                                      find_folder_window);
983
984         return (window != NULL) ? MODEST_WINDOW (window->data) : NULL;
985 }
986
987 static gboolean
988 modest_gtk_window_mgr_screen_is_on (ModestWindowMgr *self)
989 {
990         ModestGtkWindowMgrPrivate *priv = NULL;
991
992         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
993
994         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
995
996         return TRUE;
997 }
998
999 static void
1000 modest_gtk_window_mgr_create_caches (ModestWindowMgr *self)
1001 {
1002         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
1003
1004         modest_accounts_window_pre_create ();
1005
1006         MODEST_WINDOW_MGR_CLASS(parent_class)->create_caches (self);
1007 }
1008
1009 static gboolean
1010 modest_gtk_window_mgr_close_all_but_initial (ModestWindowMgr *self)
1011 {
1012         ModestWindow *top;
1013         ModestGtkWindowMgrPrivate *priv;
1014
1015         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(self);
1016
1017         /* Exit if there are no windows */
1018         if (!modest_window_mgr_get_num_windows (self)) {
1019                 g_warning ("%s: unable to close, there are no windows", __FUNCTION__);
1020                 return FALSE;
1021         }
1022
1023         /* Close active modals */
1024         if (!_modest_window_mgr_close_active_modals (self)) {
1025                 g_debug ("%s: unable to close some dialogs", __FUNCTION__);
1026                 return FALSE;
1027         }
1028
1029         /* Close all but first */
1030         top = modest_window_mgr_get_current_top (self);
1031         if (!MODEST_IS_ACCOUNTS_WINDOW (top))
1032                 close_all_but_first ((gpointer) priv->shell);
1033
1034         /* If some cannot be closed return */
1035         top = modest_window_mgr_get_current_top (self);
1036         if (!MODEST_IS_ACCOUNTS_WINDOW (top)) {
1037                 g_debug ("%s: could not close some windows", __FUNCTION__);
1038                 return FALSE;
1039         }
1040
1041         return TRUE;
1042 }
1043
1044 GtkWidget *
1045 modest_gtk_window_mgr_get_shell (ModestGtkWindowMgr *self)
1046 {
1047         ModestGtkWindowMgrPrivate *priv;
1048
1049         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(self);
1050
1051         return priv->shell;
1052 }