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