* some cleanups for debugging:
[modest] / src / widgets / modest-window-mgr.c
1 /* Copyright (c) 2006,2007 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 "modest-window-mgr.h"
32 #include "modest-runtime.h"
33 #include "modest-tny-folder.h"
34 #include "modest-ui-actions.h"
35 #include "modest-platform.h"
36 #include "widgets/modest-main-window.h"
37 #include "widgets/modest-msg-edit-window.h"
38 #include "widgets/modest-msg-view-window.h"
39 #include "modest-debug.h"
40
41
42 /* 'private'/'protected' functions */
43 static void modest_window_mgr_class_init (ModestWindowMgrClass *klass);
44 static void modest_window_mgr_init       (ModestWindowMgr *obj);
45 static void modest_window_mgr_finalize   (GObject *obj);
46
47 static gboolean on_window_destroy        (ModestWindow *window,
48                                           GdkEvent *event,
49                                           ModestWindowMgr *self);
50
51 static gboolean on_modal_window_close    (GtkWidget *widget,
52                                           GdkEvent *event,
53                                           gpointer user_data);
54
55 static void     on_modal_dialog_close    (GtkDialog *dialog,
56                                           gint arg1,
57                                           gpointer user_data);
58
59 static const gchar* get_show_toolbar_key (GType window_type,
60                                           gboolean fullscreen);
61
62 /* list my signals  */
63 enum {
64         WINDOW_LIST_EMPTY_SIGNAL,
65         NUM_SIGNALS
66 };
67
68 typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate;
69 struct _ModestWindowMgrPrivate {
70         GList        *window_list;
71
72         ModestWindow *main_window;
73
74         GMutex       *queue_lock;
75         GQueue       *modal_windows;
76         
77         gboolean     fullscreen_mode;
78         
79         GSList       *windows_that_prevent_hibernation;
80         GSList       *preregistered_uids;
81         GHashTable   *destroy_handlers;
82         GHashTable   *viewer_handlers;
83         
84         guint        closing_time;
85
86         GSList       *modal_handler_uids;
87 };
88 #define MODEST_WINDOW_MGR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
89                                                MODEST_TYPE_WINDOW_MGR, \
90                                                ModestWindowMgrPrivate))
91 /* globals */
92 static GObjectClass *parent_class = NULL;
93
94 /* uncomment the following if you have defined any signals */
95 static guint signals[NUM_SIGNALS] = {0};
96
97 GType
98 modest_window_mgr_get_type (void)
99 {
100         static GType my_type = 0;
101         if (!my_type) {
102                 static const GTypeInfo my_info = {
103                         sizeof(ModestWindowMgrClass),
104                         NULL,           /* base init */
105                         NULL,           /* base finalize */
106                         (GClassInitFunc) modest_window_mgr_class_init,
107                         NULL,           /* class finalize */
108                         NULL,           /* class data */
109                         sizeof(ModestWindowMgr),
110                         1,              /* n_preallocs */
111                         (GInstanceInitFunc) modest_window_mgr_init,
112                         NULL
113                 };
114                 my_type = g_type_register_static (G_TYPE_OBJECT,
115                                                   "ModestWindowMgr",
116                                                   &my_info, 0);
117         }
118         return my_type;
119 }
120
121 static void
122 modest_window_mgr_class_init (ModestWindowMgrClass *klass)
123 {
124         GObjectClass *gobject_class;
125         gobject_class = (GObjectClass*) klass;
126
127         parent_class            = g_type_class_peek_parent (klass);
128         gobject_class->finalize = modest_window_mgr_finalize;
129
130         g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate));
131
132
133         /**
134          * ModestWindowMgr::window-list-empty
135          * @self: the #ModestWindowMgr that emits the signal
136          * @user_data: user data set when the signal handler was connected
137          *
138          * Issued whenever the window list becomes empty
139          */
140         signals[WINDOW_LIST_EMPTY_SIGNAL] =
141                 g_signal_new ("window-list-empty",
142                               G_TYPE_FROM_CLASS (gobject_class),
143                               G_SIGNAL_RUN_FIRST,
144                               G_STRUCT_OFFSET (ModestWindowMgrClass, window_list_empty),
145                               NULL, NULL,
146                               g_cclosure_marshal_VOID__VOID,
147                               G_TYPE_NONE, 0);
148 }
149
150 static void
151 modest_window_mgr_init (ModestWindowMgr *obj)
152 {
153         ModestWindowMgrPrivate *priv;
154
155         priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
156         priv->window_list = NULL;
157         priv->main_window = NULL;
158         priv->fullscreen_mode = FALSE;
159
160         priv->modal_windows = g_queue_new ();
161         priv->queue_lock = g_mutex_new ();
162         
163         priv->preregistered_uids = NULL;
164
165         /* Could not initialize it from gconf, singletons are not
166            ready yet */
167         priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);   
168         priv->viewer_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
169
170         priv->closing_time = 0;
171
172         priv->modal_handler_uids = NULL;
173 }
174
175 static void
176 modest_window_mgr_finalize (GObject *obj)
177 {
178         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
179
180         if (priv->window_list) {
181                 GList *iter = priv->window_list;
182                 /* unregister pending windows */
183                 while (iter) {
184                         modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), 
185                                                              MODEST_WINDOW (iter->data));
186                         iter = g_list_next (iter);
187                 }
188                 g_list_free (priv->window_list);
189                 priv->window_list = NULL;
190         }
191
192         g_slist_foreach (priv->preregistered_uids, (GFunc)g_free, NULL);
193         g_slist_free (priv->preregistered_uids);
194
195         
196         /* Free the hash table with the handlers */
197         if (priv->destroy_handlers) {
198                 g_hash_table_destroy (priv->destroy_handlers);
199                 priv->destroy_handlers = NULL;
200         }
201
202         if (priv->viewer_handlers) {
203                 g_hash_table_destroy (priv->viewer_handlers);
204                 priv->viewer_handlers = NULL;
205         }
206
207         modest_signal_mgr_disconnect_all_and_destroy (priv->modal_handler_uids);
208
209         if (priv->modal_windows) {
210                 g_mutex_lock (priv->queue_lock);
211                 g_queue_free (priv->modal_windows);
212                 priv->modal_windows = NULL;
213                 g_mutex_unlock (priv->queue_lock);
214         }
215         g_mutex_free (priv->queue_lock);
216         
217         /* Do not unref priv->main_window because it does not hold a
218            new reference */
219
220         
221         G_OBJECT_CLASS(parent_class)->finalize (obj);
222 }
223
224 ModestWindowMgr*
225 modest_window_mgr_new (void)
226 {
227         return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_WINDOW_MGR, NULL));
228 }
229
230
231
232
233 /* do we have uid? */
234 static gboolean
235 has_uid (GSList *list, const gchar *uid)
236 {
237         GSList *cursor = list;
238
239         if (!uid)
240                 return FALSE;
241         
242         while (cursor) {
243                 if (cursor->data && strcmp (cursor->data, uid) == 0)
244                         return TRUE;
245                 cursor = g_slist_next (cursor);
246         }
247         return FALSE;
248 }
249
250
251 /* remove all from the list have have uid = uid */
252 static GSList*
253 remove_uid (GSList *list, const gchar *uid)
254 {
255         GSList *cursor = list, *start = list;
256         
257         if (!uid)
258                 return FALSE;
259         
260         while (cursor) {
261                 GSList *next = g_slist_next (cursor);
262                 if (cursor->data && strcmp (cursor->data, uid) == 0) {
263                         g_free (cursor->data);
264                         start = g_slist_delete_link (start, cursor);
265                 }
266                 cursor = next;
267         }
268         return start;
269 }
270
271
272 static GSList *
273 append_uid (GSList *list, const gchar *uid)
274 {
275         return g_slist_append (list, g_strdup(uid));
276 }
277
278
279
280 void 
281 modest_window_mgr_register_header (ModestWindowMgr *self,  TnyHeader *header, const gchar *alt_uid)
282 {
283         ModestWindowMgrPrivate *priv;
284         gchar* uid;
285         
286         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
287         g_return_if_fail (TNY_IS_HEADER(header));
288                 
289         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
290         uid = modest_tny_folder_get_header_unique_id (header);
291
292         if (uid == NULL)
293                 uid = g_strdup (alt_uid);
294         
295         if (!has_uid (priv->preregistered_uids, uid)) {
296                 g_debug ("registering new uid %s", uid);
297                 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
298         } else
299                 g_debug ("already had uid %s", uid);
300         
301         g_free (uid);
302 }
303
304 void 
305 modest_window_mgr_unregister_header (ModestWindowMgr *self,  TnyHeader *header)
306 {
307         ModestWindowMgrPrivate *priv;
308         gchar* uid;
309         
310         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
311         g_return_if_fail (TNY_IS_HEADER(header));
312                 
313         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
314         uid = modest_tny_folder_get_header_unique_id (header);
315
316         if (!has_uid (priv->preregistered_uids, uid)) {
317                 g_debug ("trying to unregister non-existing uid %s", uid);
318                 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
319         } else
320                 g_debug ("unregistering uid %s", uid);
321         
322         if (has_uid (priv->preregistered_uids, uid)) {
323                 priv->preregistered_uids = remove_uid (priv->preregistered_uids, uid);
324                 if (has_uid (priv->preregistered_uids, uid))
325                         g_debug ("BUG: uid %s NOT removed", uid);
326                 else
327                         g_debug ("uid %s removed", uid);
328         }
329                 
330         g_free (uid);
331 }
332
333
334 #define MODEST_WINDOW_HELP_ID_PARAM "help-id"
335
336 void
337 modest_window_mgr_register_help_id (ModestWindowMgr *self, GtkWindow *win, const gchar* help_id)
338 {
339         /* we don't need 'self', but for API consistency... */
340         g_return_if_fail (self && MODEST_IS_WINDOW_MGR(self));
341
342         g_return_if_fail (win && GTK_IS_WINDOW(win));
343         g_return_if_fail (help_id);
344         
345         g_object_set_data_full (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM,
346                                 g_strdup(help_id), g_free);
347 }
348
349
350 const gchar*
351 modest_window_mgr_get_help_id (ModestWindowMgr *self, GtkWindow *win)
352 {
353         const gchar* help_id = NULL;
354
355         /* we don't need 'self', but for API consistency... */
356         g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR(self), NULL);
357         
358         g_return_val_if_fail (win, NULL);
359         g_return_val_if_fail (GTK_IS_WINDOW(win), NULL);
360         
361         if (MODEST_IS_MAIN_WINDOW (win)) {
362                 GtkWidget *folder_view;
363                 TnyFolderStore *folder_store;
364                 
365                 /* Get selected folder */
366                 folder_view = modest_main_window_get_child_widget (MODEST_MAIN_WINDOW (win),
367                                                                    MODEST_MAIN_WINDOW_WIDGET_TYPE_FOLDER_VIEW);
368                 folder_store = modest_folder_view_get_selected (MODEST_FOLDER_VIEW (folder_view));
369
370                 /* Switch help_id */
371                 if (folder_store && TNY_IS_FOLDER (folder_store)) {
372                         help_id = modest_tny_folder_get_help_id (TNY_FOLDER (folder_store));
373                         if (!help_id)
374                                 g_warning ("%s: BUG: did not get a valid help_id", __FUNCTION__);
375                 }
376                 if (folder_store)
377                         g_object_unref (folder_store);
378         }
379
380         if (!help_id)
381                 help_id = g_object_get_data (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM);
382                 
383         return help_id;
384 }
385
386 static gint
387 compare_msguids (ModestWindow *win,
388                  const gchar *uid)
389 {
390         const gchar *msg_uid;
391
392         if ((!MODEST_IS_MSG_EDIT_WINDOW (win)) && (!MODEST_IS_MSG_VIEW_WINDOW (win)))
393                 return 1;
394
395         /* Get message uid from msg window */
396         if (MODEST_IS_MSG_EDIT_WINDOW (win)) {
397                 msg_uid = modest_msg_edit_window_get_message_uid (MODEST_MSG_EDIT_WINDOW (win));
398                 if (msg_uid && uid &&!strcmp (msg_uid, uid))
399                         return 0;
400         } else {
401                 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
402         }
403         
404         if (msg_uid && uid &&!strcmp (msg_uid, uid))
405                 return 0;
406         else
407                 return 1;
408 }
409
410 void
411 modest_window_mgr_close_all_windows (ModestWindowMgr *self)
412 {
413         ModestWindowMgrPrivate *priv = NULL;
414         GList *wins = NULL;
415         gboolean ret_value = FALSE;
416         
417         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
418         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
419         
420         /* delete-event handler already removes window_list item, */
421         /* so no next its required on this loop  */
422         wins = priv->window_list;
423         while (wins) {          
424                 g_signal_emit_by_name (G_OBJECT (wins->data), "delete-event", NULL, &ret_value);
425
426                 wins = priv->window_list;
427         }
428 }
429
430
431 gboolean
432 modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
433                                           ModestWindow **win)
434 {
435         ModestWindowMgrPrivate *priv = NULL;
436         gchar* uid = NULL;
437         gboolean has_header, has_window = FALSE;
438         GList *item = NULL;
439
440         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
441         g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
442         
443         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
444
445         uid = modest_tny_folder_get_header_unique_id (header);
446         
447         if (win)
448                 *win = NULL;
449
450         has_header = has_uid (priv->preregistered_uids, uid);
451         
452         item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids);
453         if (item) {
454                 has_window = TRUE;
455                 if (win) {
456                         if ((!MODEST_IS_MSG_VIEW_WINDOW(item->data)) && 
457                             (!MODEST_IS_MSG_EDIT_WINDOW (item->data)))
458                                 g_debug ("not a valid window!");
459                         else {
460                                 g_debug ("found a window");
461                                 *win = MODEST_WINDOW (item->data);
462                         }
463                 }
464         }
465         g_free (uid);
466         
467         return has_header || has_window;
468 }
469
470 static const gchar *
471 get_show_toolbar_key (GType window_type,
472                       gboolean fullscreen)
473 {
474         const gchar *key = NULL;
475
476         if (window_type == MODEST_TYPE_MAIN_WINDOW)
477                 key = (fullscreen) ? 
478                         MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR_FULLSCREEN :
479                         MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR;
480         else if (window_type == MODEST_TYPE_MSG_VIEW_WINDOW)
481                 key = (fullscreen) ? 
482                         MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN :
483                         MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR;
484         else if (window_type ==  MODEST_TYPE_MSG_EDIT_WINDOW)
485                 key = (fullscreen) ? 
486                         MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR_FULLSCREEN :
487                         MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR;
488         else
489                 g_return_val_if_reached (NULL);
490
491         return key;
492 }
493
494 void 
495 modest_window_mgr_register_window (ModestWindowMgr *self, 
496                                    ModestWindow *window)
497 {
498         GList *win;
499         ModestWindowMgrPrivate *priv;
500         gint *handler_id;
501         const gchar *key;
502
503         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
504         g_return_if_fail (GTK_IS_WINDOW (window));
505
506         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
507
508         win = g_list_find (priv->window_list, window);
509         if (win) {
510                 /* this is for the case we want to register the window and it was already
511                  * registered. We leave silently.
512                  */
513                 return;
514         }
515         
516         /* Check that it's not a second main window */
517         if (MODEST_IS_MAIN_WINDOW (window)) {
518                 if (priv->main_window) {
519                         g_warning ("%s: trying to register a second main window",
520                                    __FUNCTION__);
521                         return;
522                 } else {
523                         priv->main_window = window;
524                 }
525         }
526
527         /* remove from the list of pre-registered uids */
528         if (MODEST_IS_MSG_VIEW_WINDOW(window)) {
529                 const gchar *uid = modest_msg_view_window_get_message_uid
530                         (MODEST_MSG_VIEW_WINDOW (window));
531
532                 if (!has_uid (priv->preregistered_uids, uid)) 
533                         g_debug ("weird: no uid for window (%s)", uid);
534                 
535                 g_debug ("registering window for %s", uid ? uid : "<none>");
536                 
537                 priv->preregistered_uids = 
538                         remove_uid (priv->preregistered_uids,
539                                     modest_msg_view_window_get_message_uid
540                                     (MODEST_MSG_VIEW_WINDOW (window)));
541         } else if (MODEST_IS_MSG_EDIT_WINDOW(window)) {
542                 const gchar *uid = modest_msg_edit_window_get_message_uid
543                         (MODEST_MSG_EDIT_WINDOW (window));
544                 
545                 g_debug ("registering window for %s", uid);
546
547                 priv->preregistered_uids = 
548                         remove_uid (priv->preregistered_uids,
549                                     modest_msg_edit_window_get_message_uid
550                                     (MODEST_MSG_EDIT_WINDOW (window)));
551         }
552         
553         /* Add to list. Keep a reference to the window */
554         g_object_ref (window);
555         priv->window_list = g_list_prepend (priv->window_list, window);
556
557         /* Listen to object destruction */
558         handler_id = g_malloc0 (sizeof (gint));
559         *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
560         g_hash_table_insert (priv->destroy_handlers, window, handler_id);
561
562         /* If there is a msg view window, let the main window listen the msg-changed signal */
563         if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) {
564                 gulong *handler;
565                 handler = g_malloc0 (sizeof (gulong));
566                 *handler = g_signal_connect (window, "msg-changed", 
567                                              G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed), 
568                                              priv->main_window);
569                 g_hash_table_insert (priv->viewer_handlers, window, handler);
570         }
571
572         /* Put into fullscreen if needed */
573         if (priv->fullscreen_mode)
574                 gtk_window_fullscreen (GTK_WINDOW (window));
575
576         /* Show/hide toolbar & fullscreen */    
577         key = get_show_toolbar_key (G_TYPE_FROM_INSTANCE (window), priv->fullscreen_mode);
578         modest_window_show_toolbar (window, modest_conf_get_bool (modest_runtime_get_conf (), key, NULL));
579 }
580
581 static gboolean
582 on_window_destroy (ModestWindow *window, 
583                    GdkEvent *event,
584                    ModestWindowMgr *self)
585 {
586         gint dialog_response = GTK_RESPONSE_NONE;
587
588         /* Specific stuff first */
589         if (MODEST_IS_MAIN_WINDOW (window)) {
590                 ModestWindowMgrPrivate *priv;
591                 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
592
593                 /* If more than one window already opened */
594                 if (g_list_length (priv->window_list) > 1) {
595
596                         /* Create the confirmation dialog MSG-NOT308 */
597                         dialog_response = modest_platform_run_confirmation_dialog (
598                                         GTK_WINDOW (window), _("emev_nc_close_windows"));
599
600                         /* If the user wants to close all the windows */
601                         if ((dialog_response == GTK_RESPONSE_OK) 
602                                         || (dialog_response == GTK_RESPONSE_ACCEPT) 
603                                         || (dialog_response == GTK_RESPONSE_YES))
604                                 {
605                                         GList *iter = priv->window_list;
606                                         do {
607                                                 if (iter->data != window) {
608                                                         GList *tmp = iter->next;
609                                                         on_window_destroy (MODEST_WINDOW (iter->data),
610                                                                         event,
611                                                                         self);
612                                                         iter = tmp;
613                                                 } else {
614                                                         iter = g_list_next (iter);
615                                                 }
616                                         } while (iter);
617                                 }
618                         else
619                                 {
620                                         return TRUE;
621                                 }
622                 }
623         }
624         else {
625                 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
626                         gboolean sent = FALSE;
627                         gint response = GTK_RESPONSE_ACCEPT;
628                         sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
629                         /* Save currently edited message to Drafts if it was not sent */
630                         if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
631
632                                 /* Raise the window if it's minimized */
633                                 if (!gtk_window_has_toplevel_focus (GTK_WINDOW (window)))
634                                         gtk_window_present (GTK_WINDOW (window));
635                                 
636                                 response =
637                                         modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
638                                                                                  _("mcen_nc_no_email_message_modified_save_changes"));
639                                 /* Save to drafts */
640                                 if (response != GTK_RESPONSE_CANCEL)                            
641                                         modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));                            
642                         }
643                 }
644         }
645
646         /* Unregister window */
647         modest_window_mgr_unregister_window (self, window);
648         
649         return FALSE;
650 }
651
652 static void
653 disconnect_msg_changed (gpointer key, 
654                         gpointer value, 
655                         gpointer user_data)
656 {
657         guint handler_id;
658         handler_id = GPOINTER_TO_UINT(value);
659         
660         if (key && G_IS_OBJECT(key))
661                 g_signal_handler_disconnect (G_OBJECT (key), handler_id);
662 }
663
664 void 
665 modest_window_mgr_unregister_window (ModestWindowMgr *self, 
666                                      ModestWindow *window)
667 {
668         GList *win;
669         ModestWindowMgrPrivate *priv;
670         gulong *tmp, handler_id;
671
672         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
673         g_return_if_fail (MODEST_IS_WINDOW (window));
674
675         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
676
677         win = g_list_find (priv->window_list, window);
678         if (!win) {
679                 g_warning ("Trying to unregister a window that has not being registered yet");
680                 return;
681         }
682
683         /* If it's the main window unset it */
684         if (priv->main_window == window) {
685                 priv->main_window = NULL;
686
687                 /* Disconnect all emissions of msg-changed */
688                 if (priv->viewer_handlers) {
689                         g_hash_table_foreach (priv->viewer_handlers, 
690                                               disconnect_msg_changed, 
691                                               NULL);
692                         g_hash_table_destroy (priv->viewer_handlers);
693                         priv->viewer_handlers = NULL;
694                 }
695         }
696
697         /* Remove the viewer window handler from the hash table. The
698            HashTable could not exist if the main window was closeed
699            when there were other windows remaining */
700         if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
701                 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
702                 /* If the viewer was created without a main window
703                    (for example when opening a message through D-Bus
704                    the viewer handlers was not registered */
705                 if (tmp) {
706                         g_signal_handler_disconnect (window, *tmp);
707                         g_hash_table_remove (priv->viewer_handlers, window);
708                 }
709         }
710
711         /* Save state */
712         modest_window_save_state (window);
713
714         /* Remove from list & hash table */
715         priv->window_list = g_list_remove_link (priv->window_list, win);
716         tmp = g_hash_table_lookup (priv->destroy_handlers, window);
717         handler_id = *tmp;
718         g_hash_table_remove (priv->destroy_handlers, window);
719
720         /* Disconnect the "delete-event" handler, we won't need it anymore */
721         g_signal_handler_disconnect (window, handler_id);
722
723         /* Disconnect all the window signals */
724         modest_window_disconnect_signals (window);
725         
726         /* Destroy the window */
727         gtk_widget_destroy (win->data);
728         
729         /* If there are no more windows registered emit the signal */
730         if (priv->window_list == NULL)
731                 g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0);
732 }
733
734
735
736 void
737 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
738                                        gboolean on)
739 {
740         ModestWindowMgrPrivate *priv;
741         GList *win = NULL;
742         ModestConf *conf;
743
744         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
745
746         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
747
748         /* If there is no change do nothing */
749         if (priv->fullscreen_mode == on)
750                 return;
751
752         priv->fullscreen_mode = on;
753
754         conf = modest_runtime_get_conf ();
755
756         /* Update windows */
757         win = priv->window_list;
758         while (win) {
759                 gboolean show;
760                 const gchar *key = NULL;
761
762                 /* Getting this from gconf everytime is not that
763                    expensive, we'll do it just a few times */
764                 key = get_show_toolbar_key (G_TYPE_FROM_INSTANCE (win->data), on);
765                 show = modest_conf_get_bool (conf, key, NULL);
766
767                 /* Set fullscreen/unfullscreen */
768                 if (on)
769                         gtk_window_fullscreen (GTK_WINDOW (win->data));
770                 else
771                         gtk_window_unfullscreen (GTK_WINDOW (win->data));
772
773                 /* Show/Hide toolbar */
774                 modest_window_show_toolbar (MODEST_WINDOW (win->data), show);
775
776                 win = g_list_next (win);
777         }
778 }
779
780 gboolean
781 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
782 {
783         ModestWindowMgrPrivate *priv;
784
785         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
786
787         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
788
789         return priv->fullscreen_mode;
790 }
791
792 void 
793 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
794                                  GType window_type,
795                                  gboolean show_toolbars,
796                                  gboolean fullscreen)
797 {
798         ModestWindowMgrPrivate *priv;
799         ModestConf *conf;
800         const gchar *key = NULL;
801
802         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
803
804         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
805         conf = modest_runtime_get_conf ();
806
807         /* If nothing changes then return */
808         key = get_show_toolbar_key (window_type, fullscreen);
809         conf = modest_runtime_get_conf ();
810         if (modest_conf_get_bool (conf, key, NULL) == show_toolbars)
811                 return;
812
813         /* Save in conf */
814         modest_conf_set_bool (conf, key, show_toolbars, NULL);
815
816         /* Apply now if the view mode is the right one */
817         if ((fullscreen && priv->fullscreen_mode) ||
818             (!fullscreen && !priv->fullscreen_mode)) {
819
820                 GList *win = priv->window_list;
821
822                 while (win) {
823                         if (G_TYPE_FROM_INSTANCE (win->data) == window_type)
824                                 modest_window_show_toolbar (MODEST_WINDOW (win->data),
825                                                             show_toolbars);
826                         win = g_list_next (win);
827                 }
828         }
829 }
830
831 ModestWindow*  
832 modest_window_mgr_get_main_window (ModestWindowMgr *self, gboolean create)
833 {
834         ModestWindowMgrPrivate *priv;
835         
836         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
837         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
838         
839         /* create the main window, if it hasn't been created yet */
840         if (!priv->main_window && create) {
841                 /* modest_window_mgr_register_window will set priv->main_window */
842                 modest_window_mgr_register_window (self, modest_main_window_new ());
843                 MODEST_DEBUG_BLOCK(
844                         g_debug ("%s: created main window: %p\n", __FUNCTION__, priv->main_window);
845                 );
846         }
847         
848         return priv->main_window;
849 }
850
851
852 gboolean
853 modest_window_mgr_main_window_exists  (ModestWindowMgr *self)
854 {
855         ModestWindowMgrPrivate *priv;
856         
857         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
858         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
859
860         return priv->main_window != NULL;
861 }
862
863
864 GtkWindow *
865 modest_window_mgr_get_modal (ModestWindowMgr *self)
866 {
867         ModestWindowMgrPrivate *priv;
868         
869         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
870         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
871
872         return g_queue_peek_head (priv->modal_windows);
873 }
874
875
876 void
877 modest_window_mgr_set_modal (ModestWindowMgr *self, 
878                              GtkWindow *window)
879 {
880         GtkWindow *old_modal;
881         ModestWindowMgrPrivate *priv;
882
883         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
884         g_return_if_fail (GTK_IS_WINDOW (window));
885
886         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
887         g_mutex_lock (priv->queue_lock);
888         old_modal = g_queue_peek_head (priv->modal_windows);
889         g_mutex_unlock (priv->queue_lock);
890
891         if (!old_modal) {       
892                 gtk_window_set_modal (window, TRUE);
893         } else {
894                 /* un-modalize the old one; the one on top should be the
895                  * modal one */
896                 gtk_window_set_transient_for (window, GTK_WINDOW(old_modal));   
897                 gtk_window_set_modal (window, TRUE);
898         }
899
900         /* this will be the new modal window */
901         g_mutex_lock (priv->queue_lock);
902         g_queue_push_head (priv->modal_windows, window);
903         g_mutex_unlock (priv->queue_lock);
904
905         if (GTK_IS_DIALOG (window))
906                 /* Note that response is not always enough because it
907                    could be captured and removed easily by dialogs but
908                    works for most of situations */
909                 priv->modal_handler_uids = 
910                         modest_signal_mgr_connect (priv->modal_handler_uids, 
911                                                    G_OBJECT (window), 
912                                                    "response",
913                                                    G_CALLBACK (on_modal_dialog_close), 
914                                                    self);
915         else
916                 priv->modal_handler_uids = 
917                         modest_signal_mgr_connect (priv->modal_handler_uids, 
918                                                    G_OBJECT (window), 
919                                                    "delete-event",
920                                                    G_CALLBACK (on_modal_window_close), 
921                                                    self);
922 }
923
924
925 static void
926 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
927 {
928         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
929         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
930         
931         /* Forget this window,
932          * so hibernation will be allowed again if no windows are remembered: */
933         priv->windows_that_prevent_hibernation =
934                 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
935 }
936
937 static void
938 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
939 {
940         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
941         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
942         
943         GtkWindow *window = GTK_WINDOW (widget);
944         
945         priv->windows_that_prevent_hibernation = 
946                         g_slist_append (priv->windows_that_prevent_hibernation, window);
947         
948         /* Allow hibernation again when the window has been hidden: */
949         g_signal_connect (window, "hide", 
950                 G_CALLBACK (on_nonhibernating_window_hide), self);
951 }
952
953 void
954 modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
955                                                                   GtkWindow *window)
956 {
957         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
958         
959         if (GTK_WIDGET_VISIBLE(window)) {
960                 on_nonhibernating_window_show (GTK_WIDGET (window), self);
961         } else {
962                 /* Wait for it to be shown: */
963                 g_signal_connect (window, "show", 
964                         G_CALLBACK (on_nonhibernating_window_show), self);      
965         }
966 }
967
968 gboolean
969 modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
970 {
971         g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
972         
973         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
974         
975         /* Prevent hibernation if any open windows are currently 
976          * preventing hibernation: */
977         return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
978 }
979
980
981 void
982 modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
983 {
984         g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
985         
986         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
987
988         /* Iterate over all windows */
989         GList *win = priv->window_list;
990         while (win) {
991                 ModestWindow *window = MODEST_WINDOW (win->data);
992                 if (window) {
993                         /* This calls the vfunc, 
994                          * so each window can do its own thing: */
995                         modest_window_save_state (window);
996                 }       
997                 
998                 win = g_list_next (win);
999         }
1000 }
1001
1002 static gboolean
1003 idle_top_modal (gpointer data)
1004 {
1005         ModestWindowMgr *self = MODEST_WINDOW_MGR (data);
1006         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
1007         GtkWindow *topmost;
1008
1009         /* Get the top modal */
1010         g_mutex_lock (priv->queue_lock);
1011         topmost = (GtkWindow *) g_queue_peek_head (priv->modal_windows);
1012         g_mutex_unlock (priv->queue_lock);
1013
1014         /* Show it */
1015         if (topmost)
1016                 gtk_window_present (topmost);
1017
1018         return FALSE;
1019 }
1020
1021 static void
1022 remove_modal_from_queue (GtkWidget *widget,
1023                          ModestWindowMgr *self)
1024 {
1025         ModestWindowMgrPrivate *priv;
1026         GList *item = NULL;
1027
1028         priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
1029
1030         /* Remove from queue. We don't use remove, because we want to
1031            exit if the widget does not belong to the queue */
1032         g_mutex_lock (priv->queue_lock);
1033         item = g_queue_find (priv->modal_windows, widget);
1034         if (!item) {
1035                 g_warning ("Trying to remove a modal window that is not registered");
1036                 g_mutex_unlock (priv->queue_lock);
1037                 return;
1038         }
1039         g_queue_unlink (priv->modal_windows, item);
1040         g_mutex_unlock (priv->queue_lock);
1041
1042         /* Disconnect handler */
1043         priv->modal_handler_uids = 
1044                 modest_signal_mgr_disconnect (priv->modal_handler_uids, 
1045                                               G_OBJECT (widget),
1046                                               GTK_IS_DIALOG (widget) ? 
1047                                               "response" : 
1048                                               "destroy-event");
1049
1050         /* Schedule the next one for being shown */
1051         g_idle_add (idle_top_modal, self);
1052 }
1053
1054 static gboolean
1055 on_modal_window_close (GtkWidget *widget,
1056                        GdkEvent *event,
1057                        gpointer user_data)
1058 {
1059         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
1060
1061         /* Remove modal window from queue */
1062         remove_modal_from_queue (widget, self);
1063
1064         /* Continue */
1065         return FALSE;
1066 }
1067
1068 static void
1069 on_modal_dialog_close (GtkDialog *dialog,
1070                        gint arg1,
1071                        gpointer user_data)
1072 {
1073         ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
1074
1075         /* Remove modal window from queue */
1076         remove_modal_from_queue (GTK_WIDGET (dialog), self);
1077 }
1078
1079 gint 
1080 modest_window_mgr_num_windows (ModestWindowMgr *self)
1081 {
1082         ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(self);
1083         gint num_windows = 0;
1084
1085         if (priv->window_list)
1086                 num_windows = g_list_length (priv->window_list);
1087
1088         return num_windows;
1089 }