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