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