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 void on_window_destroy (ModestWindow *window, */
47 /* ModestWindowMgr *self); */
48 static gboolean on_window_destroy (ModestWindow *window,
50 ModestWindowMgr *self);
59 typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate;
60 struct _ModestWindowMgrPrivate {
62 ModestWindow *main_window;
63 gboolean fullscreen_mode;
64 gboolean show_toolbars;
65 gboolean show_toolbars_fullscreen;
67 GSList *windows_that_prevent_hibernation;
68 GSList *preregistered_uids;
69 GHashTable *destroy_handlers;
70 GHashTable *viewer_handlers;
72 #define MODEST_WINDOW_MGR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
73 MODEST_TYPE_WINDOW_MGR, \
74 ModestWindowMgrPrivate))
76 static GObjectClass *parent_class = NULL;
78 /* uncomment the following if you have defined any signals */
79 /* static guint signals[LAST_SIGNAL] = {0}; */
82 modest_window_mgr_get_type (void)
84 static GType my_type = 0;
86 static const GTypeInfo my_info = {
87 sizeof(ModestWindowMgrClass),
89 NULL, /* base finalize */
90 (GClassInitFunc) modest_window_mgr_class_init,
91 NULL, /* class finalize */
92 NULL, /* class data */
93 sizeof(ModestWindowMgr),
95 (GInstanceInitFunc) modest_window_mgr_init,
98 my_type = g_type_register_static (G_TYPE_OBJECT,
106 modest_window_mgr_class_init (ModestWindowMgrClass *klass)
108 GObjectClass *gobject_class;
109 gobject_class = (GObjectClass*) klass;
111 parent_class = g_type_class_peek_parent (klass);
112 gobject_class->finalize = modest_window_mgr_finalize;
114 g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate));
118 modest_window_mgr_init (ModestWindowMgr *obj)
120 ModestWindowMgrPrivate *priv;
122 priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
123 priv->window_list = NULL;
124 priv->main_window = NULL;
125 priv->fullscreen_mode = FALSE;
127 priv->preregistered_uids = NULL;
129 /* Could not initialize it from gconf, singletons are not
131 priv->show_toolbars = FALSE;
132 priv->show_toolbars_fullscreen = FALSE;
133 priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
134 priv->viewer_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
138 modest_window_mgr_finalize (GObject *obj)
140 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj);
142 if (priv->window_list) {
143 GList *iter = priv->window_list;
144 /* unregister pending windows */
146 modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj),
147 MODEST_WINDOW (iter->data));
148 iter = g_list_next (iter);
150 g_list_free (priv->window_list);
151 priv->window_list = NULL;
154 g_slist_foreach (priv->preregistered_uids, (GFunc)g_free, NULL);
155 g_slist_free (priv->preregistered_uids);
158 /* Free the hash table with the handlers */
159 if (priv->destroy_handlers) {
160 g_hash_table_destroy (priv->destroy_handlers);
161 priv->destroy_handlers = NULL;
164 if (priv->viewer_handlers) {
165 g_hash_table_destroy (priv->viewer_handlers);
166 priv->viewer_handlers = NULL;
169 /* Do not unref priv->main_window because it does not hold a
172 G_OBJECT_CLASS(parent_class)->finalize (obj);
176 modest_window_mgr_new (void)
178 return MODEST_WINDOW_MGR(g_object_new(MODEST_TYPE_WINDOW_MGR, NULL));
184 /* do we have uid? */
186 has_uid (GSList *list, const gchar *uid)
188 GSList *cursor = list;
194 if (cursor->data && strcmp (cursor->data, uid) == 0)
196 cursor = g_slist_next (cursor);
202 /* remove all from the list have have uid = uid */
204 remove_uid (GSList *list, const gchar *uid)
206 GSList *cursor = list, *start = list;
212 GSList *next = g_slist_next (cursor);
213 if (cursor->data && strcmp (cursor->data, uid) == 0) {
214 g_free (cursor->data);
215 start = g_slist_delete_link (start, cursor);
224 append_uid (GSList *list, const gchar *uid)
226 return g_slist_append (list, g_strdup(uid));
232 modest_window_mgr_register_header (ModestWindowMgr *self, TnyHeader *header)
234 ModestWindowMgrPrivate *priv;
237 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
238 g_return_if_fail (TNY_IS_HEADER(header));
240 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
241 uid = modest_tny_folder_get_header_unique_id (header);
244 if (!has_uid (priv->preregistered_uids, uid)) {
245 g_debug ("registering new uid %s", uid);
246 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
248 g_debug ("already had uid %s", uid);
254 modest_window_mgr_unregister_header (ModestWindowMgr *self, TnyHeader *header)
256 ModestWindowMgrPrivate *priv;
259 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
260 g_return_if_fail (TNY_IS_HEADER(header));
262 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
263 uid = modest_tny_folder_get_header_unique_id (header);
265 if (!has_uid (priv->preregistered_uids, uid)) {
266 g_debug ("trying to unregister non-existing uid %s", uid);
267 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
269 g_debug ("unregistering uid %s", uid);
271 if (has_uid (priv->preregistered_uids, uid)) {
272 priv->preregistered_uids = remove_uid (priv->preregistered_uids, uid);
273 if (has_uid (priv->preregistered_uids, uid))
274 g_debug ("BUG: uid %s NOT removed", uid);
276 g_debug ("uid %s removed", uid);
283 compare_msguids (ModestWindow *win,
286 const gchar *msg_uid;
288 if (!MODEST_IS_MSG_VIEW_WINDOW (win))
291 /* Get message uid from msg window */
292 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
294 if (msg_uid && uid &&!strcmp (msg_uid, uid))
303 modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
306 ModestWindowMgrPrivate *priv;
308 gboolean has_header, has_window = FALSE;
311 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
312 g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
314 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
316 uid = modest_tny_folder_get_header_unique_id (header);
321 g_debug ("windows in list: %d", g_list_length (priv->window_list));
322 g_debug ("headers in list: %d", g_slist_length (priv->preregistered_uids));
324 has_header = has_uid (priv->preregistered_uids, uid);
326 item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids);
330 if (!MODEST_IS_MSG_VIEW_WINDOW(item->data))
331 g_debug ("not a valid window!");
333 g_debug ("found a window");
334 *win = MODEST_WINDOW (item->data);
340 return has_header || has_window;
346 modest_window_mgr_register_window (ModestWindowMgr *self,
347 ModestWindow *window)
349 static gboolean first_time = TRUE;
352 ModestWindowMgrPrivate *priv;
355 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
356 g_return_if_fail (MODEST_IS_WINDOW (window));
358 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
360 win = g_list_find (priv->window_list, window);
362 g_warning ("Trying to register an already registered window");
366 /* Check that it's not a second main window */
367 if (MODEST_IS_MAIN_WINDOW (window)) {
368 if (priv->main_window) {
369 g_warning ("Trying to register a second main window");
372 priv->main_window = window;
376 /* remove from the list of pre-registered uids */
377 if (MODEST_IS_MSG_VIEW_WINDOW(window)) {
378 const gchar *uid = modest_msg_view_window_get_message_uid
379 (MODEST_MSG_VIEW_WINDOW (window));
381 g_debug ("registering window for %s", uid);
383 if (!has_uid (priv->preregistered_uids, uid))
384 g_debug ("weird: no uid for window (%s)", uid);
386 priv->preregistered_uids =
387 remove_uid (priv->preregistered_uids,
388 modest_msg_view_window_get_message_uid
389 (MODEST_MSG_VIEW_WINDOW (window)));
392 /* Add to list. Keep a reference to the window */
393 g_object_ref (window);
394 priv->window_list = g_list_prepend (priv->window_list, window);
396 /* Listen to object destruction */
397 handler_id = g_malloc0 (sizeof (gint));
398 *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
399 g_hash_table_insert (priv->destroy_handlers, window, handler_id);
401 /* If there is a msg view window, let the main window listen the msg-changed signal */
402 if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) {
404 handler = g_malloc0 (sizeof (gulong));
405 *handler = g_signal_connect (window, "msg-changed",
406 G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed),
408 g_hash_table_insert (priv->viewer_handlers, window, handler);
411 /* Put into fullscreen if needed */
412 if (priv->fullscreen_mode)
413 gtk_window_fullscreen (GTK_WINDOW (window));
417 ModestConf *conf = modest_runtime_get_conf ();
418 priv->show_toolbars =
419 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
420 priv->show_toolbars_fullscreen =
421 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
425 /* Show/hide toolbar */
426 if (priv->fullscreen_mode)
427 show = priv->show_toolbars_fullscreen;
429 show = priv->show_toolbars;
430 modest_window_show_toolbar (window, show);
434 /* on_window_destroy (ModestWindow *window, ModestWindowMgr *self) */
436 on_window_destroy (ModestWindow *window,
438 ModestWindowMgr *self)
440 /* Specific stuff first */
441 if (MODEST_IS_MAIN_WINDOW (window)) {
442 ModestWindowMgrPrivate *priv;
443 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
445 /* If more than one window already opened */
446 if (g_list_length (priv->window_list) > 1) {
448 /* If the user wants to close all the windows */
449 if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
450 GList *iter = priv->window_list;
452 if (iter->data != window) {
453 GList *tmp = iter->next;
454 on_window_destroy (MODEST_WINDOW (iter->data),
459 iter = g_list_next (iter);
466 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
467 gboolean sent = FALSE;
468 gint response = GTK_RESPONSE_ACCEPT;
469 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
470 /* Save currently edited message to Drafts if it was not sent */
471 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
474 modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
475 _("mcen_nc_no_email_message_modified_save_changes"));
477 if (response != GTK_RESPONSE_CANCEL)
478 modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
484 /* Save configuration state (TODO: why edit window does not require this function ?) */
485 if (!MODEST_IS_MSG_EDIT_WINDOW (window))
486 modest_window_save_state (MODEST_WINDOW(window));
489 /* Unregister window */
490 modest_window_mgr_unregister_window (self, window);
496 disconnect_msg_changed (gpointer key,
502 handler_id = (gulong *) value;
503 g_signal_handler_disconnect (G_OBJECT (key), *handler_id);
508 modest_window_mgr_unregister_window (ModestWindowMgr *self,
509 ModestWindow *window)
512 ModestWindowMgrPrivate *priv;
513 gint *tmp, handler_id;
515 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
516 g_return_if_fail (MODEST_IS_WINDOW (window));
518 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
520 win = g_list_find (priv->window_list, window);
522 g_warning ("Trying to unregister a window that has not being registered yet");
526 /* If it's the main window unset it */
527 if (priv->main_window == window) {
528 priv->main_window = NULL;
530 /* Disconnect all emissions of msg-changed */
531 g_hash_table_foreach (priv->viewer_handlers,
532 disconnect_msg_changed,
534 g_hash_table_destroy (priv->viewer_handlers);
535 priv->viewer_handlers = NULL;
539 modest_window_save_state (window);
541 /* Remove from list & hash table */
542 priv->window_list = g_list_remove_link (priv->window_list, win);
543 tmp = g_hash_table_lookup (priv->destroy_handlers, window);
545 g_hash_table_remove (priv->destroy_handlers, window);
547 /* Disconnect the "delete-event" handler, we won't need it anymore */
548 g_signal_handler_disconnect (window, handler_id);
550 /* Destroy the window */
551 gtk_widget_destroy (win->data);
553 /* If there are no more windows registered then exit program */
554 if (priv->window_list == NULL) {
555 ModestConf *conf = modest_runtime_get_conf ();
557 /* Save show toolbar status */
558 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN,
559 priv->show_toolbars_fullscreen, NULL);
560 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR,
561 priv->show_toolbars, NULL);
564 /* FIXME: do we ever need to do this here? */
565 if (gtk_main_level() > 0)
571 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
574 ModestWindowMgrPrivate *priv;
577 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
579 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
581 /* If there is no change do nothing */
582 if (priv->fullscreen_mode == on)
585 priv->fullscreen_mode = on;
588 win = priv->window_list;
591 gtk_window_fullscreen (GTK_WINDOW (win->data));
592 modest_window_show_toolbar (MODEST_WINDOW (win->data),
593 priv->show_toolbars_fullscreen);
595 gtk_window_unfullscreen (GTK_WINDOW (win->data));
596 modest_window_show_toolbar (MODEST_WINDOW (win->data),
597 priv->show_toolbars);
599 win = g_list_next (win);
604 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
606 ModestWindowMgrPrivate *priv;
608 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
610 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
612 return priv->fullscreen_mode;
616 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
617 gboolean show_toolbars,
620 ModestWindowMgrPrivate *priv;
622 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
624 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
626 /* If nothing changes then return. Otherwise cache it, do not
627 save to GConf for the moment, it will be done when all
628 windows become unregistered in order to avoid unnecessary
631 if (priv->show_toolbars_fullscreen == show_toolbars)
634 priv->show_toolbars_fullscreen = show_toolbars;
636 if (priv->show_toolbars == show_toolbars)
639 priv->show_toolbars = show_toolbars;
642 /* Apply now if the view mode is the right one */
643 if ((fullscreen && priv->fullscreen_mode) ||
644 (!fullscreen && !priv->fullscreen_mode)) {
646 GList *win = priv->window_list;
649 modest_window_show_toolbar (MODEST_WINDOW (win->data),
651 win = g_list_next (win);
657 modest_window_mgr_get_main_window (ModestWindowMgr *self)
659 ModestWindowMgrPrivate *priv;
661 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
663 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
665 return priv->main_window;
669 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
671 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
672 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
674 /* Forget this window,
675 * so hibernation will be allowed again if no windows are remembered: */
676 priv->windows_that_prevent_hibernation =
677 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
681 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
683 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
684 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
686 GtkWindow *window = GTK_WINDOW (widget);
688 priv->windows_that_prevent_hibernation =
689 g_slist_append (priv->windows_that_prevent_hibernation, window);
691 /* Allow hibernation again when the window has been hidden: */
692 g_signal_connect (window, "hide",
693 G_CALLBACK (on_nonhibernating_window_hide), self);
696 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
699 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
701 if (GTK_WIDGET_VISIBLE(window)) {
702 on_nonhibernating_window_show (GTK_WIDGET (window), self);
704 /* Wait for it to be shown: */
705 g_signal_connect (window, "show",
706 G_CALLBACK (on_nonhibernating_window_show), self);
710 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
712 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
714 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
716 /* Prevent hibernation if any open windows are currently
717 * preventing hibernation: */
718 return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
722 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
724 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
726 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
728 /* Iterate over all windows */
729 GList *win = priv->window_list;
731 ModestWindow *window = MODEST_WINDOW (win->data);
733 /* This calls the vfunc,
734 * so each window can do its own thing: */
735 modest_window_save_state (window);
738 win = g_list_next (win);