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