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