Fix modest_utils_show_dialog_and_forget for gtk and modest shell
[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         gtk_widget_show (priv->shell);
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         ModestWindow *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
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         ModestGtkWindowMgrPrivate *priv;
831
832         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
833         g_return_if_fail (GTK_IS_WINDOW (window));
834
835         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
836
837         gtk_window_set_modal (window, TRUE);
838
839         if (GTK_IS_WINDOW (parent)) {
840                 gtk_window_set_transient_for (window, parent);
841         } else {
842                 gtk_window_set_transient_for (window, GTK_WINDOW (priv->shell));
843         }
844         gtk_window_set_destroy_with_parent (window, TRUE);
845 }
846
847 static void
848 close_all_but_first (gpointer data)
849 {
850         gint num_windows, i;
851         gboolean retval;
852         ModestGtkWindowMgrPrivate *priv;
853
854         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(data);
855         num_windows = modest_shell_count_windows (MODEST_SHELL (priv->shell));
856
857         for (i = 0; i < (num_windows - 1); i++) {
858                 ModestWindow *current_top;
859
860                 /* Close window */
861                 current_top = modest_shell_peek_window (MODEST_SHELL (priv->shell));
862                 retval = modest_shell_delete_window (MODEST_SHELL (priv->shell), current_top);
863         }
864 }
865
866 static gboolean
867 on_idle_close_all_but_first (gpointer data)
868 {
869         gdk_threads_enter ();
870         close_all_but_first (data);
871         gdk_threads_leave ();
872
873         return FALSE;
874 }
875
876 static void
877 on_account_removed (TnyAccountStore *acc_store,
878                     TnyAccount *account,
879                     gpointer user_data)
880 {
881         ModestWindow *current_top;
882         ModestGtkWindowMgrPrivate *priv;
883
884         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (user_data);
885
886         /* Ignore transport account removals */
887         if (TNY_IS_TRANSPORT_ACCOUNT (account))
888                 return;
889
890         current_top = (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
891
892         /* if we're showing the header view of the currently deleted
893            account, or the outbox and we deleted the last account,
894            then close the window */
895         if (current_top &&
896             (MODEST_IS_HEADER_WINDOW (current_top) ||
897              MODEST_IS_FOLDER_WINDOW (current_top))) {
898                     const gchar *acc_name;
899
900                     acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
901
902                     /* We emit it in an idle, because sometimes this
903                        function could called when the account settings
904                        dialog is about to close but still there. That
905                        modal dialog would otherwise, prevent the
906                        windows from being closed */
907                     if (!strcmp (acc_name, modest_window_get_active_account (current_top)))
908                             g_idle_add (on_idle_close_all_but_first, (gpointer) priv->shell);
909         }
910 }
911
912 static ModestWindow *
913 modest_gtk_window_mgr_show_initial_window (ModestWindowMgr *self)
914 {
915         ModestWindow *initial_window = NULL;
916         ModestGtkWindowMgrPrivate *priv;
917
918         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
919
920         /* Connect to the account store "account-removed" signal". We
921            do this here because in the init the singletons are still
922            not initialized properly */
923         if (!g_signal_handler_is_connected (modest_runtime_get_account_store (),
924                                             priv->accounts_handler)) {
925                 priv->accounts_handler = g_signal_connect (modest_runtime_get_account_store (),
926                                                            "account-removed",
927                                                            G_CALLBACK (on_account_removed),
928                                                            self);
929         }
930
931         /* Return accounts window */
932         initial_window = MODEST_WINDOW (modest_accounts_window_new ());
933         modest_window_mgr_register_window (self, initial_window, NULL);
934
935         return initial_window;
936 }
937
938
939 static ModestWindow *
940 modest_gtk_window_mgr_get_current_top (ModestWindowMgr *self)
941 {
942         ModestGtkWindowMgrPrivate *priv;
943
944         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
945         return (ModestWindow *) modest_shell_peek_window (MODEST_SHELL (priv->shell));
946 }
947
948 static gint 
949 find_folder_window (gconstpointer a,
950                     gconstpointer b)
951 {
952         return (MODEST_IS_FOLDER_WINDOW (a)) ? 0 : 1;
953 }
954
955 static ModestWindow *
956 modest_gtk_window_mgr_get_folder_window (ModestWindowMgr *self)
957 {
958         ModestGtkWindowMgrPrivate *priv;
959         GList *window;
960
961         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), NULL);
962
963         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
964
965         window = g_list_find_custom (priv->window_list,
966                                      NULL,
967                                      find_folder_window);
968
969         return (window != NULL) ? MODEST_WINDOW (window->data) : NULL;
970 }
971
972 static gboolean
973 modest_gtk_window_mgr_screen_is_on (ModestWindowMgr *self)
974 {
975         ModestGtkWindowMgrPrivate *priv = NULL;
976
977         g_return_val_if_fail (MODEST_IS_GTK_WINDOW_MGR (self), FALSE);
978
979         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE (self);
980
981         return TRUE;
982 }
983
984 static void
985 modest_gtk_window_mgr_create_caches (ModestWindowMgr *self)
986 {
987         g_return_if_fail (MODEST_IS_GTK_WINDOW_MGR (self));
988
989         modest_accounts_window_pre_create ();
990
991         MODEST_WINDOW_MGR_CLASS(parent_class)->create_caches (self);
992 }
993
994 static gboolean
995 modest_gtk_window_mgr_close_all_but_initial (ModestWindowMgr *self)
996 {
997         ModestWindow *top;
998         ModestGtkWindowMgrPrivate *priv;
999
1000         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(self);
1001
1002         /* Exit if there are no windows */
1003         if (!modest_window_mgr_get_num_windows (self)) {
1004                 g_warning ("%s: unable to close, there are no windows", __FUNCTION__);
1005                 return FALSE;
1006         }
1007
1008         /* Close active modals */
1009         if (!_modest_window_mgr_close_active_modals (self)) {
1010                 g_debug ("%s: unable to close some dialogs", __FUNCTION__);
1011                 return FALSE;
1012         }
1013
1014         /* Close all but first */
1015         top = modest_window_mgr_get_current_top (self);
1016         if (!MODEST_IS_ACCOUNTS_WINDOW (top))
1017                 close_all_but_first ((gpointer) priv->shell);
1018
1019         /* If some cannot be closed return */
1020         top = modest_window_mgr_get_current_top (self);
1021         if (!MODEST_IS_ACCOUNTS_WINDOW (top)) {
1022                 g_debug ("%s: could not close some windows", __FUNCTION__);
1023                 return FALSE;
1024         }
1025
1026         return TRUE;
1027 }
1028
1029 GtkWidget *
1030 modest_gtk_window_mgr_get_shell (ModestGtkWindowMgr *self)
1031 {
1032         ModestGtkWindowMgrPrivate *priv;
1033
1034         priv = MODEST_GTK_WINDOW_MGR_GET_PRIVATE(self);
1035
1036         return priv->shell;
1037 }