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