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