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"
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_EDIT_WINDOW (win)) && (!MODEST_IS_MSG_VIEW_WINDOW (win)))
289 /* Get message uid from msg window */
290 if (MODEST_IS_MSG_EDIT_WINDOW (win)) {
291 msg_uid = modest_msg_edit_window_get_message_uid (MODEST_MSG_EDIT_WINDOW (win));
293 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
296 if (msg_uid && uid &&!strcmp (msg_uid, uid))
305 modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
308 ModestWindowMgrPrivate *priv;
310 gboolean has_header, has_window = FALSE;
313 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
314 g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
316 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
318 uid = modest_tny_folder_get_header_unique_id (header);
323 /* g_debug ("windows in list: %d", g_list_length (priv->window_list)); */
324 /* g_debug ("headers in list: %d", g_slist_length (priv->preregistered_uids)); */
326 has_header = has_uid (priv->preregistered_uids, uid);
328 item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids);
332 if ((!MODEST_IS_MSG_VIEW_WINDOW(item->data)) &&
333 (!MODEST_IS_MSG_EDIT_WINDOW (item->data)))
334 g_debug ("not a valid window!");
336 g_debug ("found a window");
337 *win = MODEST_WINDOW (item->data);
343 return has_header || has_window;
349 modest_window_mgr_register_window (ModestWindowMgr *self,
350 ModestWindow *window)
352 static gboolean first_time = TRUE;
355 ModestWindowMgrPrivate *priv;
358 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
359 g_return_if_fail (MODEST_IS_WINDOW (window));
361 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
363 win = g_list_find (priv->window_list, window);
365 g_warning ("Trying to register an already registered window");
369 /* Check that it's not a second main window */
370 if (MODEST_IS_MAIN_WINDOW (window)) {
371 if (priv->main_window) {
372 g_warning ("Trying to register a second main window");
375 priv->main_window = window;
379 /* remove from the list of pre-registered uids */
380 if (MODEST_IS_MSG_VIEW_WINDOW(window)) {
381 const gchar *uid = modest_msg_view_window_get_message_uid
382 (MODEST_MSG_VIEW_WINDOW (window));
384 g_debug ("registering window for %s", uid);
386 if (!has_uid (priv->preregistered_uids, uid))
387 g_debug ("weird: no uid for window (%s)", uid);
389 priv->preregistered_uids =
390 remove_uid (priv->preregistered_uids,
391 modest_msg_view_window_get_message_uid
392 (MODEST_MSG_VIEW_WINDOW (window)));
393 } else if (MODEST_IS_MSG_EDIT_WINDOW(window)) {
394 const gchar *uid = modest_msg_edit_window_get_message_uid
395 (MODEST_MSG_EDIT_WINDOW (window));
397 g_debug ("registering window for %s", uid);
399 if (!has_uid (priv->preregistered_uids, uid))
400 g_debug ("weird: no uid for window (%s)", uid);
402 priv->preregistered_uids =
403 remove_uid (priv->preregistered_uids,
404 modest_msg_edit_window_get_message_uid
405 (MODEST_MSG_EDIT_WINDOW (window)));
408 /* Add to list. Keep a reference to the window */
409 g_object_ref (window);
410 priv->window_list = g_list_prepend (priv->window_list, window);
412 /* Listen to object destruction */
413 handler_id = g_malloc0 (sizeof (gint));
414 *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
415 g_hash_table_insert (priv->destroy_handlers, window, handler_id);
417 /* If there is a msg view window, let the main window listen the msg-changed signal */
418 if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) {
420 handler = g_malloc0 (sizeof (gulong));
421 *handler = g_signal_connect (window, "msg-changed",
422 G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed),
424 g_hash_table_insert (priv->viewer_handlers, window, handler);
427 /* Put into fullscreen if needed */
428 if (priv->fullscreen_mode)
429 gtk_window_fullscreen (GTK_WINDOW (window));
433 ModestConf *conf = modest_runtime_get_conf ();
434 priv->show_toolbars =
435 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
436 priv->show_toolbars_fullscreen =
437 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
441 /* Show/hide toolbar */
442 if (priv->fullscreen_mode)
443 show = priv->show_toolbars_fullscreen;
445 show = priv->show_toolbars;
446 modest_window_show_toolbar (window, show);
450 on_window_destroy (ModestWindow *window,
452 ModestWindowMgr *self)
454 /* Specific stuff first */
455 if (MODEST_IS_MAIN_WINDOW (window)) {
456 ModestWindowMgrPrivate *priv;
457 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
459 /* If more than one window already opened */
460 if (g_list_length (priv->window_list) > 1) {
462 /* If the user wants to close all the windows */
463 if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
464 GList *iter = priv->window_list;
466 if (iter->data != window) {
467 GList *tmp = iter->next;
468 on_window_destroy (MODEST_WINDOW (iter->data),
473 iter = g_list_next (iter);
480 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
481 gboolean sent = FALSE;
482 gint response = GTK_RESPONSE_ACCEPT;
483 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
484 /* Save currently edited message to Drafts if it was not sent */
485 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
488 modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
489 _("mcen_nc_no_email_message_modified_save_changes"));
491 if (response != GTK_RESPONSE_CANCEL)
492 modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
498 /* Save configuration state (TODO: why edit window does not require this function ?) */
499 if (!MODEST_IS_MSG_EDIT_WINDOW (window))
500 modest_window_save_state (MODEST_WINDOW(window));
503 /* Unregister window */
504 modest_window_mgr_unregister_window (self, window);
510 disconnect_msg_changed (gpointer key,
516 handler_id = (gulong *) value;
517 g_signal_handler_disconnect (G_OBJECT (key), *handler_id);
522 modest_window_mgr_unregister_window (ModestWindowMgr *self,
523 ModestWindow *window)
526 ModestWindowMgrPrivate *priv;
527 gulong *tmp, handler_id;
529 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
530 g_return_if_fail (MODEST_IS_WINDOW (window));
532 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
534 win = g_list_find (priv->window_list, window);
536 g_warning ("Trying to unregister a window that has not being registered yet");
540 /* If it's the main window unset it */
541 if (priv->main_window == window) {
542 priv->main_window = NULL;
544 /* Disconnect all emissions of msg-changed */
545 g_hash_table_foreach (priv->viewer_handlers,
546 disconnect_msg_changed,
548 g_hash_table_destroy (priv->viewer_handlers);
549 priv->viewer_handlers = NULL;
552 /* Remove the viewer window handler from the hash table. The
553 HashTable could not exist if the main window was closeed
554 when there were other windows remaining */
555 if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) {
556 tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window);
557 g_signal_handler_disconnect (window, *tmp);
558 g_hash_table_remove (priv->viewer_handlers, window);
562 modest_window_save_state (window);
564 /* Remove from list & hash table */
565 priv->window_list = g_list_remove_link (priv->window_list, win);
566 tmp = g_hash_table_lookup (priv->destroy_handlers, window);
568 g_hash_table_remove (priv->destroy_handlers, window);
570 /* Disconnect the "delete-event" handler, we won't need it anymore */
571 g_signal_handler_disconnect (window, handler_id);
573 /* Disconnect all the window signals */
574 modest_window_disconnect_signals (window);
576 /* Destroy the window */
577 gtk_widget_destroy (win->data);
579 /* If there are no more windows registered then exit program */
580 if (priv->window_list == NULL) {
581 ModestConf *conf = modest_runtime_get_conf ();
583 /* Save show toolbar status */
584 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN,
585 priv->show_toolbars_fullscreen, NULL);
586 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR,
587 priv->show_toolbars, NULL);
590 /* FIXME: do we ever need to do this here? */
591 if (gtk_main_level() > 0)
597 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
600 ModestWindowMgrPrivate *priv;
603 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
605 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
607 /* If there is no change do nothing */
608 if (priv->fullscreen_mode == on)
611 priv->fullscreen_mode = on;
614 win = priv->window_list;
617 gtk_window_fullscreen (GTK_WINDOW (win->data));
618 modest_window_show_toolbar (MODEST_WINDOW (win->data),
619 priv->show_toolbars_fullscreen);
621 gtk_window_unfullscreen (GTK_WINDOW (win->data));
622 modest_window_show_toolbar (MODEST_WINDOW (win->data),
623 priv->show_toolbars);
625 win = g_list_next (win);
630 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
632 ModestWindowMgrPrivate *priv;
634 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
636 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
638 return priv->fullscreen_mode;
642 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
643 gboolean show_toolbars,
646 ModestWindowMgrPrivate *priv;
648 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
650 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
652 /* If nothing changes then return. Otherwise cache it, do not
653 save to GConf for the moment, it will be done when all
654 windows become unregistered in order to avoid unnecessary
657 if (priv->show_toolbars_fullscreen == show_toolbars)
660 priv->show_toolbars_fullscreen = show_toolbars;
662 if (priv->show_toolbars == show_toolbars)
665 priv->show_toolbars = show_toolbars;
668 /* Apply now if the view mode is the right one */
669 if ((fullscreen && priv->fullscreen_mode) ||
670 (!fullscreen && !priv->fullscreen_mode)) {
672 GList *win = priv->window_list;
675 modest_window_show_toolbar (MODEST_WINDOW (win->data),
677 win = g_list_next (win);
683 modest_window_mgr_get_main_window (ModestWindowMgr *self)
685 ModestWindowMgrPrivate *priv;
687 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
688 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
690 /* create the main window, if it hasn't been created yet */
691 if (!priv->main_window) {
692 g_debug ("%s: creating main window\n", __FUNCTION__);
693 modest_window_mgr_register_window (self, modest_main_window_new ());
696 return priv->main_window;
700 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
702 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
703 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
705 /* Forget this window,
706 * so hibernation will be allowed again if no windows are remembered: */
707 priv->windows_that_prevent_hibernation =
708 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
712 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
714 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
715 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
717 GtkWindow *window = GTK_WINDOW (widget);
719 priv->windows_that_prevent_hibernation =
720 g_slist_append (priv->windows_that_prevent_hibernation, window);
722 /* Allow hibernation again when the window has been hidden: */
723 g_signal_connect (window, "hide",
724 G_CALLBACK (on_nonhibernating_window_hide), self);
727 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
730 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
732 if (GTK_WIDGET_VISIBLE(window)) {
733 on_nonhibernating_window_show (GTK_WIDGET (window), self);
735 /* Wait for it to be shown: */
736 g_signal_connect (window, "show",
737 G_CALLBACK (on_nonhibernating_window_show), self);
741 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
743 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
745 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
747 /* Prevent hibernation if any open windows are currently
748 * preventing hibernation: */
749 return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
753 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
755 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
757 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
759 /* Iterate over all windows */
760 GList *win = priv->window_list;
762 ModestWindow *window = MODEST_WINDOW (win->data);
764 /* This calls the vfunc,
765 * so each window can do its own thing: */
766 modest_window_save_state (window);
769 win = g_list_next (win);