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);
243 if (!has_uid (priv->preregistered_uids, uid))
244 priv->preregistered_uids = append_uid (priv->preregistered_uids, uid);
250 modest_window_mgr_unregister_header (ModestWindowMgr *self, TnyHeader *header)
252 ModestWindowMgrPrivate *priv;
255 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
256 g_return_if_fail (TNY_IS_HEADER(header));
258 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
259 uid = modest_tny_folder_get_header_unique_id (header);
261 if (has_uid (priv->preregistered_uids, uid))
262 priv->preregistered_uids = remove_uid (priv->preregistered_uids, uid);
268 compare_msguids (ModestWindow *win,
271 const gchar *msg_uid;
273 if (!MODEST_IS_MSG_VIEW_WINDOW (win))
276 /* Get message uid from msg window */
277 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win));
279 if (msg_uid && uid &&!strcmp (msg_uid, uid))
288 modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header,
291 ModestWindowMgrPrivate *priv;
293 gboolean retval = FALSE;
296 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
297 g_return_val_if_fail (TNY_IS_HEADER(header), FALSE);
299 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
301 uid = modest_tny_folder_get_header_unique_id (header);
303 /* first, look for the window */
304 /* note, the UID cannot be in both the window list and the preregistered uid list */
305 if (priv->window_list) {
306 item = g_list_find_custom (priv->window_list,
307 uid, (GCompareFunc) compare_msguids);
311 *win = item ? MODEST_WINDOW(item->data) : NULL;
315 /* IF It's not in the window list. maybe it's in our uid list... */
316 retval = retval || has_uid (priv->preregistered_uids, uid);
326 modest_window_mgr_register_window (ModestWindowMgr *self,
327 ModestWindow *window)
329 static gboolean first_time = TRUE;
332 ModestWindowMgrPrivate *priv;
335 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
336 g_return_if_fail (MODEST_IS_WINDOW (window));
338 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
340 win = g_list_find (priv->window_list, window);
342 g_warning ("Trying to register an already registered window");
346 /* Check that it's not a second main window */
347 if (MODEST_IS_MAIN_WINDOW (window)) {
348 if (priv->main_window) {
349 g_warning ("Trying to register a second main window");
352 priv->main_window = window;
356 /* remove from the list of pre-registered uids */
357 if (MODEST_IS_MSG_VIEW_WINDOW(window)) {
358 priv->preregistered_uids =
359 remove_uid (priv->preregistered_uids,
360 modest_msg_view_window_get_message_uid
361 (MODEST_MSG_VIEW_WINDOW (window)));
364 /* Add to list. Keep a reference to the window */
365 g_object_ref (window);
366 priv->window_list = g_list_prepend (priv->window_list, window);
368 /* Listen to object destruction */
369 handler_id = g_malloc0 (sizeof (gint));
370 *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
371 g_hash_table_insert (priv->destroy_handlers, window, handler_id);
373 /* If there is a msg view window, let the main window listen the msg-changed signal */
374 if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) {
376 handler = g_malloc0 (sizeof (gulong));
377 *handler = g_signal_connect (window, "msg-changed",
378 G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed),
380 g_hash_table_insert (priv->viewer_handlers, window, handler);
383 /* Put into fullscreen if needed */
384 if (priv->fullscreen_mode)
385 gtk_window_fullscreen (GTK_WINDOW (window));
389 ModestConf *conf = modest_runtime_get_conf ();
390 priv->show_toolbars =
391 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL);
392 priv->show_toolbars_fullscreen =
393 modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL);
397 /* Show/hide toolbar */
398 if (priv->fullscreen_mode)
399 show = priv->show_toolbars_fullscreen;
401 show = priv->show_toolbars;
402 modest_window_show_toolbar (window, show);
406 /* on_window_destroy (ModestWindow *window, ModestWindowMgr *self) */
408 on_window_destroy (ModestWindow *window,
410 ModestWindowMgr *self)
412 /* Specific stuff first */
413 if (MODEST_IS_MAIN_WINDOW (window)) {
414 ModestWindowMgrPrivate *priv;
415 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
417 /* If more than one window already opened */
418 if (g_list_length (priv->window_list) > 1) {
420 /* If the user wants to close all the windows */
421 if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) {
422 GList *iter = priv->window_list;
424 if (iter->data != window) {
425 GList *tmp = iter->next;
426 on_window_destroy (MODEST_WINDOW (iter->data),
431 iter = g_list_next (iter);
438 if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
439 gboolean sent = FALSE;
440 gint response = GTK_RESPONSE_ACCEPT;
441 sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window));
442 /* Save currently edited message to Drafts if it was not sent */
443 if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) {
446 modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
447 _("mcen_nc_no_email_message_modified_save_changes"));
449 if (response != GTK_RESPONSE_CANCEL)
450 modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window));
456 /* Save configuration state (TODO: why edit window does not require this function ?) */
457 if (!MODEST_IS_MSG_EDIT_WINDOW (window))
458 modest_window_save_state (MODEST_WINDOW(window));
461 /* Unregister window */
462 modest_window_mgr_unregister_window (self, window);
468 disconnect_msg_changed (gpointer key,
474 handler_id = (gulong *) value;
475 g_signal_handler_disconnect (G_OBJECT (key), *handler_id);
480 modest_window_mgr_unregister_window (ModestWindowMgr *self,
481 ModestWindow *window)
484 ModestWindowMgrPrivate *priv;
485 gint *tmp, handler_id;
487 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
488 g_return_if_fail (MODEST_IS_WINDOW (window));
490 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
492 win = g_list_find (priv->window_list, window);
494 g_warning ("Trying to unregister a window that has not being registered yet");
498 /* If it's the main window unset it */
499 if (priv->main_window == window) {
500 priv->main_window = NULL;
502 /* Disconnect all emissions of msg-changed */
503 g_hash_table_foreach (priv->viewer_handlers,
504 disconnect_msg_changed,
506 g_hash_table_destroy (priv->viewer_handlers);
507 priv->viewer_handlers = NULL;
511 modest_window_save_state (window);
513 /* Remove from list & hash table */
514 priv->window_list = g_list_remove_link (priv->window_list, win);
515 tmp = g_hash_table_lookup (priv->destroy_handlers, window);
517 g_hash_table_remove (priv->destroy_handlers, window);
519 /* Disconnect the "delete-event" handler, we won't need it anymore */
520 g_signal_handler_disconnect (window, handler_id);
522 /* Destroy the window */
523 gtk_widget_destroy (win->data);
525 /* If there are no more windows registered then exit program */
526 if (priv->window_list == NULL) {
527 ModestConf *conf = modest_runtime_get_conf ();
529 /* Save show toolbar status */
530 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN,
531 priv->show_toolbars_fullscreen, NULL);
532 modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR,
533 priv->show_toolbars, NULL);
536 /* FIXME: do we ever need to do this here? */
537 if (gtk_main_level() > 0)
543 modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self,
546 ModestWindowMgrPrivate *priv;
549 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
551 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
553 /* If there is no change do nothing */
554 if (priv->fullscreen_mode == on)
557 priv->fullscreen_mode = on;
560 win = priv->window_list;
563 gtk_window_fullscreen (GTK_WINDOW (win->data));
564 modest_window_show_toolbar (MODEST_WINDOW (win->data),
565 priv->show_toolbars_fullscreen);
567 gtk_window_unfullscreen (GTK_WINDOW (win->data));
568 modest_window_show_toolbar (MODEST_WINDOW (win->data),
569 priv->show_toolbars);
571 win = g_list_next (win);
576 modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self)
578 ModestWindowMgrPrivate *priv;
580 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
582 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
584 return priv->fullscreen_mode;
588 modest_window_mgr_show_toolbars (ModestWindowMgr *self,
589 gboolean show_toolbars,
592 ModestWindowMgrPrivate *priv;
594 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
596 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
598 /* If nothing changes then return. Otherwise cache it, do not
599 save to GConf for the moment, it will be done when all
600 windows become unregistered in order to avoid unnecessary
603 if (priv->show_toolbars_fullscreen == show_toolbars)
606 priv->show_toolbars_fullscreen = show_toolbars;
608 if (priv->show_toolbars == show_toolbars)
611 priv->show_toolbars = show_toolbars;
614 /* Apply now if the view mode is the right one */
615 if ((fullscreen && priv->fullscreen_mode) ||
616 (!fullscreen && !priv->fullscreen_mode)) {
618 GList *win = priv->window_list;
621 modest_window_show_toolbar (MODEST_WINDOW (win->data),
623 win = g_list_next (win);
629 modest_window_mgr_get_main_window (ModestWindowMgr *self)
631 ModestWindowMgrPrivate *priv;
633 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL);
635 priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
637 return priv->main_window;
641 on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data)
643 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
644 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
646 /* Forget this window,
647 * so hibernation will be allowed again if no windows are remembered: */
648 priv->windows_that_prevent_hibernation =
649 g_slist_remove (priv->windows_that_prevent_hibernation, GTK_WINDOW(widget));
653 on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data)
655 ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data);
656 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
658 GtkWindow *window = GTK_WINDOW (widget);
660 priv->windows_that_prevent_hibernation =
661 g_slist_append (priv->windows_that_prevent_hibernation, window);
663 /* Allow hibernation again when the window has been hidden: */
664 g_signal_connect (window, "hide",
665 G_CALLBACK (on_nonhibernating_window_hide), self);
668 void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self,
671 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
673 if (GTK_WIDGET_VISIBLE(window)) {
674 on_nonhibernating_window_show (GTK_WIDGET (window), self);
676 /* Wait for it to be shown: */
677 g_signal_connect (window, "show",
678 G_CALLBACK (on_nonhibernating_window_show), self);
682 gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self)
684 g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE);
686 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
688 /* Prevent hibernation if any open windows are currently
689 * preventing hibernation: */
690 return (g_slist_length (priv->windows_that_prevent_hibernation) > 0);
694 void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self)
696 g_return_if_fail (MODEST_IS_WINDOW_MGR (self));
698 ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self);
700 /* Iterate over all windows */
701 GList *win = priv->window_list;
703 ModestWindow *window = MODEST_WINDOW (win->data);
705 /* This calls the vfunc,
706 * so each window can do its own thing: */
707 modest_window_save_state (window);
710 win = g_list_next (win);