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