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