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