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