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