1 /* Copyright (c) 2006,2007 Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
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 other impl specific header files */
41 /* 'private'/'protected' functions */
42 static void modest_window_mgr_class_init (ModestWindowMgrClass *klass);
43 static void modest_window_mgr_init (ModestWindowMgr *obj);
44 static void modest_window_mgr_finalize (GObject *obj);
46 static gboolean on_window_destroy (ModestWindow *window,
48 ModestWindowMgr *self);
57 typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate;
58 struct _ModestWindowMgrPrivate {
60 ModestWindow *main_window;
61 gboolean fullscreen_mode;
62 gboolean show_toolbars;
63 gboolean show_toolbars_fullscreen;
65 GSList *windows_that_prevent_hibernation;
66 GSList *preregistered_uids;
67 GHashTable *destroy_handlers;
68 GHashTable *viewer_handlers;
70 #define MODEST_WINDOW_MGR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
71 MODEST_TYPE_WINDOW_MGR, \
72 ModestWindowMgrPrivate))
74 static GObjectClass *parent_class = NULL;
76 /* uncomment the following if you have defined any signals */
77 /* static guint signals[LAST_SIGNAL] = {0}; */
80 modest_window_mgr_get_type (void)
82 static GType my_type = 0;
84 static const GTypeInfo my_info = {
85 sizeof(ModestWindowMgrClass),
87 NULL, /* base finalize */
88 (GClassInitFunc) modest_window_mgr_class_init,
89 NULL, /* class finalize */
90 NULL, /* class data */
91 sizeof(ModestWindowMgr),
93 (GInstanceInitFunc) modest_window_mgr_init,
96 my_type = g_type_register_static (G_TYPE_OBJECT,
104 modest_window_mgr_class_init (ModestWindowMgrClass *klass)
106 GObjectClass *gobject_class;
107 gobject_class = (GObjectClass*) klass;
109 parent_class = g_type_class_peek_parent (klass);
110 gobject_class->finalize = modest_window_mgr_finalize;
112 g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate));
116 modest_window_mgr_init (ModestWindowMgr *obj)
118 ModestWindowMgrPrivate *priv;
120 priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
121 priv->window_list = NULL;
122 priv->main_window = NULL;
123 priv->fullscreen_mode = FALSE;
125 priv->preregistered_uids = NULL;
127 /* Could not initialize it from gconf, singletons are not
129 priv->show_toolbars = FALSE;
130 priv->show_toolbars_fullscreen = FALSE;
131 priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
132 priv->viewer_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
136 modest_window_mgr_finalize (GObject *obj)
138 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
140 if (priv->window_list) {
141 GList *iter = priv->window_list;
142 /* unregister pending windows */
144 modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj),
145 MODEST_WINDOW (iter->data));
146 iter = g_list_next (iter);
148 g_list_free (priv->window_list);
149 priv->window_list = NULL;
152 g_slist_foreach (priv->preregistered_uids, (GFunc)g_free, NULL);
153 g_slist_free (priv->preregistered_uids);
156 /* Free the hash table with the handlers */
157 if (priv->destroy_handlers) {
158 g_hash_table_destroy (priv->destroy_handlers);
159 priv->destroy_handlers = NULL;
162 if (priv->viewer_handlers) {
163 g_hash_table_destroy (priv->viewer_handlers);
164 priv->viewer_handlers = NULL;
167 /* Do not unref priv->main_window because it does not hold a
170 G_OBJECT_CLASS(parent_class)->finalize (obj);
174 modest_window_mgr_new (void)
176 return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_WINDOW_MGR, NULL));
182 /* do we have uid? */
184 has_uid (GSList *list, const gchar *uid)
186 GSList *cursor = list;
192 if (cursor->data && strcmp (cursor->data, uid) == 0)
194 cursor = g_slist_next (cursor);
200 /* remove all from the list have have uid = uid */
202 remove_uid (GSList *list, const gchar *uid)
204 GSList *cursor = list, *start = list;
210 GSList *next = g_slist_next (cursor);
211 if (cursor->data && strcmp (cursor->data, uid) == 0) {
212 g_free (cursor->data);
213 start = g_slist_delete_link (start, cursor);
222 append_uid (GSList *list, const gchar *uid)
224 return g_slist_append (list, g_strdup(uid));
230 modest_window_mgr_register_header (ModestWindowMgr *self, TnyHeader *header)
232 ModestWindowMgrPrivate *priv;
235 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
236 g_return_if_fail (TNY_IS_HEADER(header));
238 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
239 uid = modest_tny_folder_get_header_unique_id (header);
242 if (!has_uid (priv->preregistered_uids, uid)) {
243 g_debug ("registering new uid %s", uid);
244 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
246 g_debug ("already had uid %s", uid);
252 modest_window_mgr_unregister_header (ModestWindowMgr *self, TnyHeader *header)
254 ModestWindowMgrPrivate *priv;
257 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
258 g_return_if_fail (TNY_IS_HEADER(header));
260 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
261 uid = modest_tny_folder_get_header_unique_id (header);
263 if (!has_uid (priv->preregistered_uids, uid)) {
264 g_debug ("trying to unregister non-existing uid %s", uid);
265 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
267 g_debug ("unregistering uid %s", uid);
269 if (has_uid (priv->preregistered_uids, uid)) {
270 priv->preregistered_uids = remove_uid (priv->preregistered_uids, uid);
271 if (has_uid (priv->preregistered_uids, uid))
272 g_debug ("BUG: uid %s NOT removed", uid);
274 g_debug ("uid %s removed", uid);
281 compare_msguids (ModestWindow *win,
284 const gchar *msg_uid;
286 if (!MODEST_IS_MSG_VIEW_WINDOW (win))
289 /* Get message uid from msg window */
290 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
292 if (msg_uid && uid &&!strcmp (msg_uid, uid))
301 modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
304 ModestWindowMgrPrivate *priv;
306 gboolean has_header, has_window = FALSE;
309 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
310 g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
312 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
314 uid = modest_tny_folder_get_header_unique_id (header);
319 /* g_debug ("windows in list: %d", g_list_length (priv->window_list)); */
320 /* g_debug ("headers in list: %d", g_slist_length (priv->preregistered_uids)); */
322 has_header = has_uid (priv->preregistered_uids, uid);
324 item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids);
328 if (!MODEST_IS_MSG_VIEW_WINDOW(item->data))
329 g_debug ("not a valid window!");
331 g_debug ("found a window");
332 *win = MODEST_WINDOW (item->data);
338 return has_header || has_window;
344 modest_window_mgr_register_window (ModestWindowMgr *self,
345 ModestWindow *window)
347 static gboolean first_time = TRUE;
350 ModestWindowMgrPrivate *priv;
353 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
354 g_return_if_fail (MODEST_IS_WINDOW (window));
356 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
358 win = g_list_find (priv->window_list, window);
360 g_warning ("Trying to register an already registered window");
364 /* Check that it's not a second main window */
365 if (MODEST_IS_MAIN_WINDOW (window)) {
366 if (priv->main_window) {
367 g_warning ("Trying to register a second main window");
370 priv->main_window = window;
374 /* remove from the list of pre-registered uids */
375 if (MODEST_IS_MSG_VIEW_WINDOW(window)) {
376 const gchar *uid = modest_msg_view_window_get_message_uid
377 (MODEST_MSG_VIEW_WINDOW (window));
379 g_debug ("registering window for %s", uid);
381 if (!has_uid (priv->preregistered_uids, uid))
382 g_debug ("weird: no uid for window (%s)", uid);
384 priv->preregistered_uids =
385 remove_uid (priv->preregistered_uids,
386 modest_msg_view_window_get_message_uid
387 (MODEST_MSG_VIEW_WINDOW (window)));
390 /* Add to list. Keep a reference to the window */
391 g_object_ref (window);
392 priv->window_list = g_list_prepend (priv->window_list, window);
394 /* Listen to object destruction */
395 handler_id = g_malloc0 (sizeof (gint));
396 *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
397 g_hash_table_insert (priv->destroy_handlers, window, handler_id);
399 /* If there is a msg view window, let the main window listen the msg-changed signal */
400 if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) {
402 handler = g_malloc0 (sizeof (gulong));
403 *handler = g_signal_connect (window, "msg-changed",
404 G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed),
406 g_hash_table_insert (priv->viewer_handlers, window, handler);
409 /* Put into fullscreen if needed */
410 if (priv->fullscreen_mode)
411 gtk_window_fullscreen (GTK_WINDOW (window));
415 ModestConf *conf = modest_runtime_get_conf ();
416 priv->show_toolbars =
417 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
418 priv->show_toolbars_fullscreen =
419 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
423 /* Show/hide toolbar */
424 if (priv->fullscreen_mode)
425 show = priv->show_toolbars_fullscreen;
427 show = priv->show_toolbars;
428 modest_window_show_toolbar (window, show);
432 on_window_destroy (ModestWindow *window,
434 ModestWindowMgr *self)
436 /* Specific stuff first */
437 if (MODEST_IS_MAIN_WINDOW (window)) {
438 ModestWindowMgrPrivate *priv;
439 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
441 /* If more than one window already opened */
442 if (g_list_length (priv->window_list) > 1) {
444 /* If the user wants to close all the windows */
445 if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
446 GList *iter = priv->window_list;
448 if (iter->data != window) {
449 GList *tmp = iter->next;
450 on_window_destroy (MODEST_WINDOW (iter->data),
455 iter = g_list_next (iter);
462 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
463 gboolean sent = FALSE;
464 gint response = GTK_RESPONSE_ACCEPT;
465 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
466 /* Save currently edited message to Drafts if it was not sent */
467 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
470 modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
471 _("mcen_nc_no_email_message_modified_save_changes"));
473 if (response != GTK_RESPONSE_CANCEL)
474 modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
480 /* Save configuration state (TODO: why edit window does not require this function ?) */
481 if (!MODEST_IS_MSG_EDIT_WINDOW (window))
482 modest_window_save_state (MODEST_WINDOW(window));
485 /* Unregister window */
486 modest_window_mgr_unregister_window (self, window);
492 disconnect_msg_changed (gpointer key,
498 handler_id = (gulong *) value;
499 g_signal_handler_disconnect (G_OBJECT (key), *handler_id);
504 modest_window_mgr_unregister_window (ModestWindowMgr *self,
505 ModestWindow *window)
508 ModestWindowMgrPrivate *priv;
509 gulong *tmp, handler_id;
511 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
512 g_return_if_fail (MODEST_IS_WINDOW (window));
514 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
516 win = g_list_find (priv->window_list, window);
518 g_warning ("Trying to unregister a window that has not being registered yet");
522 /* If it's the main window unset it */
523 if (priv->main_window == window) {
524 priv->main_window = NULL;
526 /* Disconnect all emissions of msg-changed */
527 g_hash_table_foreach (priv->viewer_handlers,
528 disconnect_msg_changed,
530 g_hash_table_destroy (priv->viewer_handlers);
531 priv->viewer_handlers = NULL;
534 /* Remove the viewer window handler from the hash table. The
535 HashTable could not exist if the main window was closeed
536 when there were other windows remaining */
537 if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
538 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
539 g_signal_handler_disconnect (window, *tmp);
540 g_hash_table_remove (priv->viewer_handlers, window);
544 modest_window_save_state (window);
546 /* Remove from list & hash table */
547 priv->window_list = g_list_remove_link (priv->window_list, win);
548 tmp = g_hash_table_lookup (priv->destroy_handlers, window);
550 g_hash_table_remove (priv->destroy_handlers, window);
552 /* Disconnect the "delete-event" handler, we won't need it anymore */
553 g_signal_handler_disconnect (window, handler_id);
555 /* Disconnect all the window signals */
556 modest_window_disconnect_signals (window);
558 /* Destroy the window */
559 gtk_widget_destroy (win->data);
561 /* If there are no more windows registered then exit program */
562 if (priv->window_list == NULL) {
563 ModestConf *conf = modest_runtime_get_conf ();
565 /* Save show toolbar status */
566 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN,
567 priv->show_toolbars_fullscreen, NULL);
568 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR,
569 priv->show_toolbars, NULL);
572 /* FIXME: do we ever need to do this here? */
573 if (gtk_main_level() > 0)
579 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
582 ModestWindowMgrPrivate *priv;
585 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
587 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
589 /* If there is no change do nothing */
590 if (priv->fullscreen_mode == on)
593 priv->fullscreen_mode = on;
596 win = priv->window_list;
599 gtk_window_fullscreen (GTK_WINDOW (win->data));
600 modest_window_show_toolbar (MODEST_WINDOW (win->data),
601 priv->show_toolbars_fullscreen);
603 gtk_window_unfullscreen (GTK_WINDOW (win->data));
604 modest_window_show_toolbar (MODEST_WINDOW (win->data),
605 priv->show_toolbars);
607 win = g_list_next (win);
612 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
614 ModestWindowMgrPrivate *priv;
616 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
618 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
620 return priv->fullscreen_mode;
624 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
625 gboolean show_toolbars,
628 ModestWindowMgrPrivate *priv;
630 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
632 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
634 /* If nothing changes then return. Otherwise cache it, do not
635 save to GConf for the moment, it will be done when all
636 windows become unregistered in order to avoid unnecessary
639 if (priv->show_toolbars_fullscreen == show_toolbars)
642 priv->show_toolbars_fullscreen = show_toolbars;
644 if (priv->show_toolbars == show_toolbars)
647 priv->show_toolbars = show_toolbars;
650 /* Apply now if the view mode is the right one */
651 if ((fullscreen && priv->fullscreen_mode) ||
652 (!fullscreen && !priv->fullscreen_mode)) {
654 GList *win = priv->window_list;
657 modest_window_show_toolbar (MODEST_WINDOW (win->data),
659 win = g_list_next (win);
665 modest_window_mgr_get_main_window (ModestWindowMgr *self)
667 ModestWindowMgrPrivate *priv;
669 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
671 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
673 return priv->main_window;
677 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
679 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
680 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
682 /* Forget this window,
683 * so hibernation will be allowed again if no windows are remembered: */
684 priv->windows_that_prevent_hibernation =
685 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
689 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
691 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
692 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
694 GtkWindow *window = GTK_WINDOW (widget);
696 priv->windows_that_prevent_hibernation =
697 g_slist_append (priv->windows_that_prevent_hibernation, window);
699 /* Allow hibernation again when the window has been hidden: */
700 g_signal_connect (window, "hide",
701 G_CALLBACK (on_nonhibernating_window_hide), self);
704 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
707 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
709 if (GTK_WIDGET_VISIBLE(window)) {
710 on_nonhibernating_window_show (GTK_WIDGET (window), self);
712 /* Wait for it to be shown: */
713 g_signal_connect (window, "show",
714 G_CALLBACK (on_nonhibernating_window_show), self);
718 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
720 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
722 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
724 /* Prevent hibernation if any open windows are currently
725 * preventing hibernation: */
726 return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
730 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
732 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
734 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
736 /* Iterate over all windows */
737 GList *win = priv->window_list;
739 ModestWindow *window = MODEST_WINDOW (win->data);
741 /* This calls the vfunc,
742 * so each window can do its own thing: */
743 modest_window_save_state (window);
746 win = g_list_next (win);