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