Do not allow to send messages to empty recipients
[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
436         g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), FALSE);
437         g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
438
439         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
440
441         /* Try to close active modal dialogs */
442         if (modest_window_mgr_get_num_windows (self) &&
443             !_modest_window_mgr_close_active_modals (self))
444                 return FALSE;
445
446         stack = hildon_window_stack_get_default ();
447         current_top = (ModestWindow *) hildon_window_stack_peek (stack);
448
449         win = g_list_find (priv->window_list, window);
450         if (win) {
451                 /* this is for the case we want to register the window
452                    and it was already registered */
453                 gtk_window_present (GTK_WINDOW (window));
454                 return FALSE;
455         }
456
457         /* Do not allow standalone editors or standalone viewers */
458         if (!current_top &&
459             (MODEST_IS_MSG_VIEW_WINDOW (window) ||
460              MODEST_IS_MSG_EDIT_WINDOW (window)))
461                 modest_window_mgr_show_initial_window (self);
462
463         if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
464                 gchar *uid;
465                 TnyHeader *header;
466
467                 uid = g_strdup (modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window)));
468                 
469                 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
470
471                 if (uid == NULL)
472                         uid = modest_tny_folder_get_header_unique_id (header);
473                 /* Embedded messages do not have uid */
474                 if (uid) {
475                         if (g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids)) {
476                                 g_debug ("%s found another view window showing the same header", __FUNCTION__);
477                                 g_free (uid);
478                                 g_object_unref (header);
479                                 return FALSE;
480                         }
481                         g_free (uid);
482                 } else if (header) {
483                         if (g_list_find_custom (priv->window_list, header, (GCompareFunc) compare_headers)) {
484                                 g_debug ("%s found another view window showing the same header", __FUNCTION__);
485                                 g_object_unref (header);
486                                 return FALSE;
487                         }
488                 }
489                 if (header)
490                         g_object_unref (header);
491         }
492
493         /* Do not go backwards */
494         if ((MODEST_IS_MSG_VIEW_WINDOW (current_top) ||
495              MODEST_IS_MSG_EDIT_WINDOW (current_top) ||
496              MODEST_IS_HEADER_WINDOW (current_top)) &&
497             (MODEST_IS_FOLDER_WINDOW (window) ||
498              MODEST_IS_ACCOUNTS_WINDOW (window) ||
499              MODEST_IS_MAILBOXES_WINDOW (window))) {
500                 gtk_window_present (GTK_WINDOW (current_top));
501                 return FALSE;
502         }
503
504         if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_FOLDER_WINDOW (window)) {
505                 gboolean retval;
506
507                 g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
508
509                 if (retval) {
510                         gtk_window_present (GTK_WINDOW (current_top));
511                         return FALSE;
512                 }
513                 current_top = (ModestWindow *) hildon_window_stack_peek (stack);
514         }
515
516         if (MODEST_IS_MAILBOXES_WINDOW (current_top) && MODEST_IS_MAILBOXES_WINDOW (window)) {
517                 gtk_window_present (GTK_WINDOW (current_top));
518                 return FALSE;
519         }
520
521         /* Mailboxes window can not replace folder windows */
522         if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_MAILBOXES_WINDOW (window)) {
523                 gtk_window_present (GTK_WINDOW (current_top));
524                 return FALSE;
525         }
526
527         /* Trying to open a folders window and a mailboxes window at
528            the same time from the accounts window is not allowed */
529         if (MODEST_IS_MAILBOXES_WINDOW (current_top) &&
530             MODEST_IS_FOLDER_WINDOW (window) &&
531             MODEST_IS_ACCOUNTS_WINDOW (parent)) {
532                 gtk_window_present (GTK_WINDOW (window));
533                 return FALSE;
534         }
535
536         if (MODEST_IS_HEADER_WINDOW (current_top) && MODEST_IS_HEADER_WINDOW (window)) {
537                 g_debug ("Trying to register a second header window is not allowed");
538                 gtk_window_present (GTK_WINDOW (current_top));
539                 return FALSE;
540         }
541
542         if (!MODEST_WINDOW_MGR_CLASS (parent_class)->register_window (self, window, parent))
543                 goto fail;
544
545         /* Add to list. Keep a reference to the window */
546         g_object_ref (window);
547         priv->window_list = g_list_prepend (priv->window_list, window);
548
549         nested_msg = MODEST_IS_MSG_VIEW_WINDOW (window) &&
550                 MODEST_IS_MSG_VIEW_WINDOW (parent);
551
552         /* Close views if they're being shown. Nevertheless we must
553            allow nested messages */
554         if (!nested_msg &&
555             (MODEST_IS_MSG_EDIT_WINDOW (current_top) ||
556              MODEST_IS_MSG_VIEW_WINDOW (current_top))) {
557                 gboolean retval;
558
559                 /* If the current view has modal dialogs then
560                    we fail to register the new view */
561                 if ((current_top != NULL) &&
562                     window_has_modals (MODEST_WINDOW (current_top))) {
563                         /* Window on top but it has opened dialogs */
564                         goto fail;
565                 }
566
567                 /* Close the current view */
568                 g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
569                 if (retval) {
570                         /* Cancelled closing top window, then we fail to register */
571                         goto fail;
572                 }
573         }
574
575         /* Listen to object destruction */
576         handler_id = g_malloc0 (sizeof (gint));
577         *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
578         g_hash_table_insert (priv->destroy_handlers, window, handler_id);
579
580         /* Show toolbar always */
581         modest_window_show_toolbar (window, TRUE);
582
583         return TRUE;
584 fail:
585         /* Add to list. Keep a reference to the window */
586         priv->window_list = g_list_remove (priv->window_list, window);
587         g_object_unref (window);
588         current_top = (ModestWindow *) hildon_window_stack_peek (stack);
589         if (current_top)
590                 gtk_window_present (GTK_WINDOW (current_top));
591         return FALSE;
592 }
593
594 static void
595 cancel_window_operations (ModestWindow *window)
596 {
597         GSList* pending_ops = NULL;
598
599         /* cancel open and receive operations */
600         pending_ops = modest_mail_operation_queue_get_by_source (modest_runtime_get_mail_operation_queue (), 
601                                                                  G_OBJECT (window));
602         while (pending_ops != NULL) {
603                 ModestMailOperationTypeOperation type;
604                 GSList* tmp_list = NULL;
605
606                 type = modest_mail_operation_get_type_operation (MODEST_MAIL_OPERATION (pending_ops->data));
607                 if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
608                     type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
609                     type == MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE) {
610                         modest_mail_operation_cancel (pending_ops->data);
611                 }
612                 g_object_unref (G_OBJECT (pending_ops->data));
613                 tmp_list = pending_ops;
614                 pending_ops = g_slist_next (pending_ops);
615                 g_slist_free_1 (tmp_list);
616         }
617 }
618
619 static gboolean
620 window_has_modals (ModestWindow *window)
621 {
622         GList *toplevels;
623         GList *node;
624         gboolean retvalue = FALSE;
625
626         /* First we fetch all toplevels */
627         toplevels = gtk_window_list_toplevels ();
628         for (node = toplevels; node != NULL; node = g_list_next (node)) {
629                 if (GTK_IS_WINDOW (node->data) &&
630                     gtk_window_get_transient_for (GTK_WINDOW (node->data)) == GTK_WINDOW (window) &&
631                     GTK_WIDGET_VISIBLE (node->data)) {
632                         retvalue = TRUE;
633                         break;
634                 }
635         }
636         g_list_free (toplevels);
637         return retvalue;
638 }
639
640 static gboolean
641 on_window_destroy (ModestWindow *window, 
642                    GdkEvent *event,
643                    ModestHildon2WindowMgr *self)
644 {
645         gboolean no_propagate = FALSE;
646
647         /* Do not close the window if it has modals on top */
648         if (!MODEST_IS_MSG_EDIT_WINDOW (window) && window_has_modals (window))
649                 return TRUE;
650
651         if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
652                 gboolean sent = FALSE;
653                 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
654                 /* Save currently edited message to Drafts if it was not sent */
655                 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
656                         ModestMsgEditWindow *edit_window;
657                         MsgData *data;
658
659                         edit_window = MODEST_MSG_EDIT_WINDOW (window);
660                         data = modest_msg_edit_window_get_msg_data (edit_window);
661
662                         if (data) {
663                                 gint parts_count;
664                                 guint64 parts_size, available_size, expected_size;
665
666                                 available_size = modest_utils_get_available_space (NULL);
667                                 modest_msg_edit_window_get_parts_size (edit_window, &parts_count, &parts_size);
668                                 expected_size = modest_tny_msg_estimate_size (data->plain_body,
669                                                                               data->html_body,
670                                                                               parts_count,
671                                                                               parts_size);
672                                 modest_msg_edit_window_free_msg_data (edit_window, data);
673                                 data = NULL;
674
675                                 /* If there is not enough space
676                                    available for saving the message
677                                    then show an error and close the
678                                    window without saving */
679                                 if (expected_size >= available_size) {
680                                         modest_platform_run_information_dialog (GTK_WINDOW (edit_window),
681                                                                                 _("mail_in_ui_save_error"),
682                                                                                 FALSE);
683                                 } else {
684                                         if (!modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window)))
685                                                 return TRUE;
686                                 }
687                         } else {
688                                 g_warning ("Edit window without message data. This is probably a bug");
689                         }
690                 }
691         }
692
693         /* Unregister window */
694         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (self), window);
695         no_propagate = FALSE;
696
697         return no_propagate;
698 }
699
700 static void
701 modest_hildon2_window_mgr_unregister_window (ModestWindowMgr *self, 
702                                              ModestWindow *window)
703 {
704         GList *win;
705         ModestHildon2WindowMgrPrivate *priv;
706         gulong *tmp, handler_id;
707         guint num_windows;
708
709         g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
710         g_return_if_fail (MODEST_IS_WINDOW (window));
711
712         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
713
714         win = g_list_find (priv->window_list, window);
715         if (!win) {
716                 g_debug ("Trying to unregister a window that has not being registered yet");
717                 return;
718         }
719
720         /* Remove the viewer window handler from the hash table. The
721            HashTable could not exist if the main window was closed
722            when there were other windows remaining */
723         if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
724                 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
725                 /* If the viewer was created without a main window
726                    (for example when opening a message through D-Bus
727                    the viewer handlers was not registered */
728                 if (tmp) {
729                         g_signal_handler_disconnect (window, *tmp);
730                         g_hash_table_remove (priv->viewer_handlers, window);
731                 }
732         }
733
734         /* Remove from list & hash table */
735         priv->window_list = g_list_remove_link (priv->window_list, win);
736         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
737         handler_id = *tmp;
738
739         g_hash_table_remove (priv->destroy_handlers, window);
740
741         /* cancel open and receive operations */
742         cancel_window_operations (window);
743
744         /* Disconnect the "window-state-event" handler, we won't need it anymore */
745         if (priv->window_state_uids) {
746                 priv->window_state_uids = 
747                         modest_signal_mgr_disconnect (priv->window_state_uids, 
748                                                       G_OBJECT (window), 
749                                                       "notify::is-topmost");
750         }
751
752         /* Disconnect the "delete-event" handler, we won't need it anymore */
753         g_signal_handler_disconnect (window, handler_id);
754
755         /* Destroy the window */
756         g_object_unref (win->data);
757         g_list_free (win);
758
759         MODEST_WINDOW_MGR_CLASS (parent_class)->unregister_window (self, window);
760
761         /* We have to get the number of windows here in order not to
762            emit the signal too many times */
763         num_windows = modest_window_mgr_get_num_windows (self);
764
765         /* If there are no more windows registered emit the signal */
766         if (num_windows == 0)
767                 g_signal_emit_by_name (self, "window-list-empty");
768 }
769
770
771 static void
772 modest_hildon2_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
773                                                gboolean on)
774 {
775         g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
776
777         return;
778 }
779
780 static gboolean
781 modest_hildon2_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
782 {
783         return FALSE;
784 }
785
786 static void 
787 modest_hildon2_window_mgr_show_toolbars (ModestWindowMgr *self,
788                                          GType window_type,
789                                          gboolean show_toolbars,
790                                          gboolean fullscreen)
791 {
792         g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
793
794         return;
795 }
796
797 static gint
798 look_for_transient (gconstpointer a,
799                     gconstpointer b)
800 {
801         GtkWindow *win, *child;
802
803         if (a == b)
804                 return 1;
805
806         child = (GtkWindow *) b;
807         win = (GtkWindow *) a;
808
809         if ((gtk_window_get_transient_for (win) == child) &&
810             GTK_WIDGET_VISIBLE (win))
811                 return 0;
812         else
813                 return 1;
814 }
815
816 static GtkWindow *
817 modest_hildon2_window_mgr_get_modal (ModestWindowMgr *self)
818 {
819         ModestHildon2WindowMgrPrivate *priv;
820         GList *toplevel_list;
821         GtkWidget *current_top, *toplevel;
822         HildonWindowStack *stack;
823
824         g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), NULL);
825         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
826
827         /* Get current top */
828         stack = hildon_window_stack_get_default ();
829         current_top = hildon_window_stack_peek (stack);
830         toplevel = current_top;
831         toplevel_list = gtk_window_list_toplevels ();
832
833         while (toplevel) {
834                 GList *parent_link;
835
836                 parent_link = g_list_find_custom (toplevel_list, toplevel, look_for_transient);
837                 if (parent_link)
838                         toplevel = (GtkWidget *) parent_link->data;
839                 else
840                         break;
841         }
842
843         if (toplevel && GTK_WIDGET_VISIBLE (toplevel) && gtk_window_get_modal ((GtkWindow *) toplevel))
844                 return (GtkWindow *) toplevel;
845         else
846                 return NULL;
847 }
848
849
850 static void
851 modest_hildon2_window_mgr_set_modal (ModestWindowMgr *self, 
852                                      GtkWindow *window,
853                                      GtkWindow *parent)
854 {
855         g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
856         g_return_if_fail (GTK_IS_WINDOW (window));
857
858         gtk_window_set_modal (window, TRUE);
859         gtk_window_set_transient_for (window, parent);
860         gtk_window_set_destroy_with_parent (window, TRUE);
861 }
862
863 static void
864 close_all_but_first (gpointer data)
865 {
866         gint num_windows, i;
867         gboolean retval;
868         HildonWindowStack *stack;
869
870         stack = hildon_window_stack_get_default ();
871         g_return_if_fail (stack);
872
873         num_windows = hildon_window_stack_size (stack);
874
875         for (i = 0; i < (num_windows - 1); i++) {
876                 GtkWidget *current_top;
877
878                 /* Close window */
879                 current_top = hildon_window_stack_peek (stack);
880                 g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
881         }
882 }
883
884 static gboolean
885 on_idle_close_all_but_first (gpointer data)
886 {
887         gdk_threads_enter ();
888         close_all_but_first (data);
889         gdk_threads_leave ();
890
891         return FALSE;
892 }
893
894 static void
895 on_account_removed (TnyAccountStore *acc_store,
896                     TnyAccount *account,
897                     gpointer user_data)
898 {
899         HildonWindowStack *stack;
900         ModestWindow *current_top;
901
902         /* Ignore transport account removals */
903         if (TNY_IS_TRANSPORT_ACCOUNT (account))
904                 return;
905
906         stack = hildon_window_stack_get_default ();
907         current_top = (ModestWindow *) hildon_window_stack_peek (stack);
908
909         /* if we're showing the header view of the currently deleted
910            account, or the outbox and we deleted the last account,
911            then close the window */
912         if (current_top &&
913             (MODEST_IS_HEADER_WINDOW (current_top) ||
914              MODEST_IS_FOLDER_WINDOW (current_top))) {
915                     const gchar *acc_name;
916
917                     acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
918
919                     /* We emit it in an idle, because sometimes this
920                        function could called when the account settings
921                        dialog is about to close but still there. That
922                        modal dialog would otherwise, prevent the
923                        windows from being closed */
924                     if (!strcmp (acc_name, modest_window_get_active_account (current_top)))
925                             g_idle_add (on_idle_close_all_but_first, NULL);
926         }
927 }
928
929 static ModestWindow *
930 modest_hildon2_window_mgr_show_initial_window (ModestWindowMgr *self)
931 {
932         ModestWindow *initial_window = NULL;
933         ModestHildon2WindowMgrPrivate *priv;
934
935         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
936
937         /* Connect to the account store "account-removed" signal". We
938            do this here because in the init the singletons are still
939            not initialized properly */
940         if (!g_signal_handler_is_connected (modest_runtime_get_account_store (),
941                                             priv->accounts_handler)) {
942                 priv->accounts_handler = g_signal_connect (modest_runtime_get_account_store (),
943                                                            "account-removed",
944                                                            G_CALLBACK (on_account_removed),
945                                                            self);
946         }
947
948         /* Return accounts window */
949         initial_window = MODEST_WINDOW (modest_accounts_window_new ());
950         modest_window_mgr_register_window (self, initial_window, NULL);
951
952         return initial_window;
953 }
954
955
956 static ModestWindow *
957 modest_hildon2_window_mgr_get_current_top (ModestWindowMgr *self)
958 {
959         HildonWindowStack *stack;
960         stack = hildon_window_stack_get_default ();
961         return (ModestWindow *) hildon_window_stack_peek (stack);
962 }
963
964 static gint 
965 find_folder_window (gconstpointer a,
966                     gconstpointer b)
967 {
968         return (MODEST_IS_FOLDER_WINDOW (a)) ? 0 : 1;
969 }
970
971 static ModestWindow *
972 modest_hildon2_window_mgr_get_folder_window (ModestWindowMgr *self)
973 {
974         ModestHildon2WindowMgrPrivate *priv;
975         GList *window;
976
977         g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), NULL);
978
979         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
980
981         window = g_list_find_custom (priv->window_list,
982                                      NULL,
983                                      find_folder_window);
984
985         return (window != NULL) ? MODEST_WINDOW (window->data) : NULL;
986 }
987
988 static gboolean
989 modest_hildon2_window_mgr_screen_is_on (ModestWindowMgr *self)
990 {
991         ModestHildon2WindowMgrPrivate *priv = NULL;
992
993         g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), FALSE);
994
995         priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
996         
997         return (priv->display_state == OSSO_DISPLAY_ON) ? TRUE : FALSE;
998 }
999
1000 static void
1001 modest_hildon2_window_mgr_create_caches (ModestWindowMgr *self)
1002 {
1003         g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
1004
1005         modest_accounts_window_pre_create ();
1006
1007         MODEST_WINDOW_MGR_CLASS(parent_class)->create_caches (self);
1008 }
1009
1010 static void
1011 osso_display_event_cb (osso_display_state_t state,
1012                        gpointer data)
1013 {
1014         ModestHildon2WindowMgrPrivate *priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (data);
1015
1016         priv->display_state = state;
1017
1018         /* Stop blinking if the screen becomes on */
1019         if (priv->display_state == OSSO_DISPLAY_ON)
1020                 modest_platform_remove_new_mail_notifications (TRUE);
1021 }
1022
1023 static gboolean
1024 modest_hildon2_window_mgr_close_all_but_initial (ModestWindowMgr *self)
1025 {
1026         ModestWindow *top;
1027
1028         /* Exit if there are no windows */
1029         if (!modest_window_mgr_get_num_windows (self)) {
1030                 g_warning ("%s: unable to close, there are no windows", __FUNCTION__);
1031                 return FALSE;
1032         }
1033
1034         /* Close active modals */
1035         if (!_modest_window_mgr_close_active_modals (self)) {
1036                 g_debug ("%s: unable to close some dialogs", __FUNCTION__);
1037                 return FALSE;
1038         }
1039
1040         /* Close all but first */
1041         top = modest_window_mgr_get_current_top (self);
1042         if (!MODEST_IS_ACCOUNTS_WINDOW (top))
1043                 close_all_but_first (NULL);
1044
1045         /* If some cannot be closed return */
1046         top = modest_window_mgr_get_current_top (self);
1047         if (!MODEST_IS_ACCOUNTS_WINDOW (top)) {
1048                 g_debug ("%s: could not close some windows", __FUNCTION__);
1049                 return FALSE;
1050         }
1051
1052         return TRUE;
1053 }