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