Move accounts 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 window_has_modals (ModestWindow *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         GtkWidget *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), shell);
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                     window_has_modals (MODEST_WINDOW (current_top))) {
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 window_has_modals (ModestWindow *window)
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 (priv->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         gboolean no_propagate = FALSE;
629
630         /* Do not close the window if it has modals on top */
631         if (!MODEST_IS_MSG_EDIT_WINDOW (window) && window_has_modals (window))
632                 return TRUE;
633
634         if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
635                 gboolean sent = FALSE;
636                 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
637                 /* Save currently edited message to Drafts if it was not sent */
638                 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
639                         ModestMsgEditWindow *edit_window;
640                         MsgData *data;
641
642                         edit_window = MODEST_MSG_EDIT_WINDOW (window);
643                         data = modest_msg_edit_window_get_msg_data (edit_window);
644
645                         if (data) {
646                                 gint parts_count;
647                                 guint64 parts_size, available_size, expected_size;
648
649                                 available_size = modest_utils_get_available_space (NULL);
650                                 modest_msg_edit_window_get_parts_size (edit_window, &parts_count, &parts_size);
651                                 expected_size = modest_tny_msg_estimate_size (data->plain_body,
652                                                                               data->html_body,
653                                                                               parts_count,
654                                                                               parts_size);
655                                 modest_msg_edit_window_free_msg_data (edit_window, data);
656                                 data = NULL;
657
658                                 /* If there is not enough space
659                                    available for saving the message
660                                    then show an error and close the
661                                    window without saving */
662                                 if (expected_size >= available_size) {
663                                         modest_platform_run_information_dialog (GTK_WINDOW (edit_window),
664                                                                                 _("mail_in_ui_save_error"),
665                                                                                 FALSE);
666                                 } else {
667                                         if (!modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window)))
668                                                 return TRUE;
669                                 }
670                         } else {
671                                 g_warning ("Edit window without message data. This is probably a bug");
672                         }
673                 }
674         }
675
676         /* Unregister window */
677         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (self), window);
678         no_propagate = FALSE;
679
680         return no_propagate;
681 }
682
683 static void
684 modest_gtk_window_mgr_unregister_window (ModestWindowMgr *self, 
685                                          ModestWindow *window)
686 {
687         GList *win;
688         ModestGtkWindowMgrPrivate *priv;
689         gulong *tmp, handler_id;
690         guint num_windows;
691
692         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
693         g_return_if_fail (MODEST_IS_WINDOW (window));
694
695         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
696
697         win = g_list_find (priv->window_list, window);
698         if (!win) {
699                 g_debug ("Trying to unregister a window that has not being registered yet");
700                 return;
701         }
702
703         /* Remove the viewer window handler from the hash table. The
704            HashTable could not exist if the main window was closed
705            when there were other windows remaining */
706         if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
707                 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
708                 /* If the viewer was created without a main window
709                    (for example when opening a message through D-Bus
710                    the viewer handlers was not registered */
711                 if (tmp) {
712                         g_signal_handler_disconnect (window, *tmp);
713                         g_hash_table_remove (priv->viewer_handlers, window);
714                 }
715         }
716
717         /* Remove from list & hash table */
718         priv->window_list = g_list_remove_link (priv->window_list, win);
719         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
720         handler_id = *tmp;
721
722         g_hash_table_remove (priv->destroy_handlers, window);
723
724         /* cancel open and receive operations */
725         cancel_window_operations (window);
726
727         /* Disconnect the "delete-event" handler, we won't need it anymore */
728         g_signal_handler_disconnect (window, handler_id);
729
730         /* Destroy the window */
731         g_object_unref (win->data);
732         g_list_free (win);
733
734         MODEST_WINDOW_MGR_CLASS (parent_class)->unregister_window (self, window);
735
736         /* We have to get the number of windows here in order not to
737            emit the signal too many times */
738         num_windows = modest_window_mgr_get_num_windows (self);
739
740         /* If there are no more windows registered emit the signal */
741         if (num_windows == 0)
742                 g_signal_emit_by_name (self, "window-list-empty");
743 }
744
745
746 static void
747 modest_gtk_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
748                                                gboolean on)
749 {
750         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
751
752         return;
753 }
754
755 static gboolean
756 modest_gtk_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
757 {
758         return FALSE;
759 }
760
761 static void 
762 modest_gtk_window_mgr_show_toolbars (ModestWindowMgr *self,
763                                          GType window_type,
764                                          gboolean show_toolbars,
765                                          gboolean fullscreen)
766 {
767         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
768
769         return;
770 }
771
772 static gint
773 look_for_transient (gconstpointer a,
774                     gconstpointer b)
775 {
776         GtkWindow *win, *child;
777
778         if (a == b)
779                 return 1;
780
781         child = (GtkWindow *) b;
782         win = (GtkWindow *) a;
783
784         if ((gtk_window_get_transient_for (win) == child) &&
785             GTK_WIDGET_VISIBLE (win))
786                 return 0;
787         else
788                 return 1;
789 }
790
791 static GtkWindow *
792 modest_gtk_window_mgr_get_modal (ModestWindowMgr *self)
793 {
794         ModestGtkWindowMgrPrivate *priv;
795         GList *toplevel_list;
796         GtkWidget *current_top, *toplevel;
797
798         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
799         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
800
801         /* Get current top */
802         toplevel = priv->shell;
803         toplevel_list = gtk_window_list_toplevels ();
804
805         while (toplevel) {
806                 GList *parent_link;
807
808                 parent_link = g_list_find_custom (toplevel_list, toplevel, look_for_transient);
809                 if (parent_link)
810                         toplevel = (GtkWidget *) parent_link->data;
811                 else
812                         break;
813         }
814
815         if (toplevel && GTK_WIDGET_VISIBLE (toplevel) && gtk_window_get_modal ((GtkWindow *) toplevel))
816                 return (GtkWindow *) toplevel;
817         else
818                 return NULL;
819 }
820
821
822 static void
823 modest_gtk_window_mgr_set_modal (ModestWindowMgr *self, 
824                                      GtkWindow *window,
825                                      GtkWindow *parent)
826 {
827         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
828         g_return_if_fail (GTK_IS_WINDOW (window));
829
830         gtk_window_set_modal (window, TRUE);
831         gtk_window_set_transient_for (window, parent);
832         gtk_window_set_destroy_with_parent (window, TRUE);
833 }
834
835 static void
836 close_all_but_first (gpointer data)
837 {
838         gint num_windows, i;
839         gboolean retval;
840         HildonWindowStack *stack;
841
842         stack = hildon_window_stack_get_default ();
843         g_return_if_fail (stack);
844
845         num_windows = hildon_window_stack_size (stack);
846
847         for (i = 0; i < (num_windows - 1); i++) {
848                 GtkWidget *current_top;
849
850                 /* Close window */
851                 current_top = modest_shell_peek_window (MODEST_SHELL (priv->shell));
852                 retval = modest_shell_delete_window (MODEST_SHELL (priv->shell), current_top);
853         }
854 }
855
856 static gboolean
857 on_idle_close_all_but_first (gpointer data)
858 {
859         gdk_threads_enter ();
860         close_all_but_first (data);
861         gdk_threads_leave ();
862
863         return FALSE;
864 }
865
866 static void
867 on_account_removed (TnyAccountStore *acc_store,
868                     TnyAccount *account,
869                     gpointer user_data)
870 {
871         ModestWindow *current_top;
872
873         /* Ignore transport account removals */
874         if (TNY_IS_TRANSPORT_ACCOUNT (account))
875                 return;
876
877         current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
878
879         /* if we're showing the header view of the currently deleted
880            account, or the outbox and we deleted the last account,
881            then close the window */
882         if (current_top &&
883             (MODEST_IS_HEADER_WINDOW (current_top) ||
884              MODEST_IS_FOLDER_WINDOW (current_top))) {
885                     const gchar *acc_name;
886
887                     acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
888
889                     /* We emit it in an idle, because sometimes this
890                        function could called when the account settings
891                        dialog is about to close but still there. That
892                        modal dialog would otherwise, prevent the
893                        windows from being closed */
894                     if (!strcmp (acc_name, modest_window_get_active_account (current_top)))
895                             g_idle_add (on_idle_close_all_but_first, NULL);
896         }
897 }
898
899 static ModestWindow *
900 modest_gtk_window_mgr_show_initial_window (ModestWindowMgr *self)
901 {
902         ModestWindow *initial_window = NULL;
903         ModestGtkWindowMgrPrivate *priv;
904
905         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
906
907         /* Connect to the account store "account-removed" signal". We
908            do this here because in the init the singletons are still
909            not initialized properly */
910         if (!g_signal_handler_is_connected (modest_runtime_get_account_store (),
911                                             priv->accounts_handler)) {
912                 priv->accounts_handler = g_signal_connect (modest_runtime_get_account_store (),
913                                                            "account-removed",
914                                                            G_CALLBACK (on_account_removed),
915                                                            self);
916         }
917
918         /* Return accounts window */
919         initial_window = MODEST_WINDOW (modest_accounts_window_new ());
920         modest_window_mgr_register_window (self, initial_window, NULL);
921
922         return initial_window;
923 }
924
925
926 static ModestWindow *
927 modest_gtk_window_mgr_get_current_top (ModestWindowMgr *self)
928 {
929         ModestWindow *initial_window = NULL;
930         ModestGtkWindowMgrPrivate *priv;
931
932         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
933         return (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
934 }
935
936 static gint 
937 find_folder_window (gconstpointer a,
938                     gconstpointer b)
939 {
940         return (MODEST_IS_FOLDER_WINDOW (a)) ? 0 : 1;
941 }
942
943 static ModestWindow *
944 modest_gtk_window_mgr_get_folder_window (ModestWindowMgr *self)
945 {
946         ModestGtkWindowMgrPrivate *priv;
947         GList *window;
948
949         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
950
951         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
952
953         window = g_list_find_custom (priv->window_list,
954                                      NULL,
955                                      find_folder_window);
956
957         return (window != NULL) ? MODEST_WINDOW (window->data) : NULL;
958 }
959
960 static gboolean
961 modest_gtk_window_mgr_screen_is_on (ModestWindowMgr *self)
962 {
963         ModestGtkWindowMgrPrivate *priv = NULL;
964
965         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
966
967         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
968
969         return TRUE;
970 }
971
972 static void
973 modest_gtk_window_mgr_create_caches (ModestWindowMgr *self)
974 {
975         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
976
977         modest_accounts_window_pre_create ();
978
979         MODEST_WINDOW_MGR_CLASS(parent_class)->create_caches (self);
980 }
981
982 static gboolean
983 modest_gtk_window_mgr_close_all_but_initial (ModestWindowMgr *self)
984 {
985         ModestWindow *top;
986
987         /* Exit if there are no windows */
988         if (!modest_window_mgr_get_num_windows (self)) {
989                 g_warning ("%s: unable to close, there are no windows", __FUNCTION__);
990                 return FALSE;
991         }
992
993         /* Close active modals */
994         if (!_modest_window_mgr_close_active_modals (self)) {
995                 g_debug ("%s: unable to close some dialogs", __FUNCTION__);
996                 return FALSE;
997         }
998
999         /* Close all but first */
1000         top = modest_window_mgr_get_current_top (self);
1001         if (!MODEST_IS_ACCOUNTS_WINDOW (top))
1002                 close_all_but_first (NULL);
1003
1004         /* If some cannot be closed return */
1005         top = modest_window_mgr_get_current_top (self);
1006         if (!MODEST_IS_ACCOUNTS_WINDOW (top)) {
1007                 g_debug ("%s: could not close some windows", __FUNCTION__);
1008                 return FALSE;
1009         }
1010
1011         return TRUE;
1012 }