X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fwidgets%2Fmodest-window-mgr.c;h=fb88abf2cb08f3584af8f5e9dbfe7660a67e792b;hp=ca27a2c41e9bc1a6c2b520acaa5ae41e46accf10;hb=f6d386ff93e929092ba105385a29d760aeba9ff7;hpb=781806c53992ccec3456d5fa90c0f319edae7667 diff --git a/src/widgets/modest-window-mgr.c b/src/widgets/modest-window-mgr.c index ca27a2c..fb88abf 100644 --- a/src/widgets/modest-window-mgr.c +++ b/src/widgets/modest-window-mgr.c @@ -36,37 +36,65 @@ #include "widgets/modest-main-window.h" #include "widgets/modest-msg-edit-window.h" #include "widgets/modest-msg-view-window.h" -/* include other impl specific header files */ +#include "modest-debug.h" + /* 'private'/'protected' functions */ static void modest_window_mgr_class_init (ModestWindowMgrClass *klass); static void modest_window_mgr_init (ModestWindowMgr *obj); static void modest_window_mgr_finalize (GObject *obj); -/* static void on_window_destroy (ModestWindow *window, */ -/* ModestWindowMgr *self); */ -static gboolean on_window_destroy (ModestWindow *window, - GdkEvent *event, - ModestWindowMgr *self); +static gboolean on_window_destroy (ModestWindow *window, + GdkEvent *event, + ModestWindowMgr *self); + +static gboolean on_modal_window_close (GtkWidget *widget, + GdkEvent *event, + gpointer user_data); + +static void on_modal_dialog_destroy (GtkObject *object, + gpointer user_data); + +static void on_modal_dialog_close (GtkDialog *dialog, + gint arg1, + gpointer user_data); + +static const gchar* get_show_toolbar_key (GType window_type, + gboolean fullscreen); /* list my signals */ enum { - /* MY_SIGNAL_1, */ - /* MY_SIGNAL_2, */ - LAST_SIGNAL + WINDOW_LIST_EMPTY_SIGNAL, + NUM_SIGNALS }; typedef struct _ModestWindowMgrPrivate ModestWindowMgrPrivate; struct _ModestWindowMgrPrivate { GList *window_list; + guint banner_counter; + ModestWindow *main_window; + + GMutex *queue_lock; + GQueue *modal_windows; + gboolean fullscreen_mode; - gboolean show_toolbars; - gboolean show_toolbars_fullscreen; GSList *windows_that_prevent_hibernation; GSList *preregistered_uids; GHashTable *destroy_handlers; + GHashTable *viewer_handlers; + GSList *window_state_uids; + + guint closing_time; + + GSList *modal_handler_uids; + GtkWidget *cached_view; + GtkWidget *cached_editor; + guint idle_load_view_id; + guint idle_load_editor_id; + + ModestWindow *current_top; }; #define MODEST_WINDOW_MGR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_WINDOW_MGR, \ @@ -75,7 +103,7 @@ struct _ModestWindowMgrPrivate { static GObjectClass *parent_class = NULL; /* uncomment the following if you have defined any signals */ -/* static guint signals[LAST_SIGNAL] = {0}; */ +static guint signals[NUM_SIGNALS] = {0}; GType modest_window_mgr_get_type (void) @@ -111,6 +139,59 @@ modest_window_mgr_class_init (ModestWindowMgrClass *klass) gobject_class->finalize = modest_window_mgr_finalize; g_type_class_add_private (gobject_class, sizeof(ModestWindowMgrPrivate)); + + + /** + * ModestWindowMgr::window-list-empty + * @self: the #ModestWindowMgr that emits the signal + * @user_data: user data set when the signal handler was connected + * + * Issued whenever the window list becomes empty + */ + signals[WINDOW_LIST_EMPTY_SIGNAL] = + g_signal_new ("window-list-empty", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ModestWindowMgrClass, window_list_empty), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static gboolean +idle_load_view (ModestWindowMgr *mgr) +{ + ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (mgr); + + priv->cached_view = g_object_new (MODEST_TYPE_MSG_VIEW_WINDOW, NULL); + priv->idle_load_view_id = 0; + return FALSE; +} + +static gboolean +idle_load_editor (ModestWindowMgr *mgr) +{ + ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (mgr); + + priv->cached_editor = g_object_new (MODEST_TYPE_MSG_EDIT_WINDOW, NULL); + priv->idle_load_editor_id = 0; + return FALSE; +} + +static void +load_new_view (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + if ((priv->cached_view == NULL) && (priv->idle_load_view_id == 0)) + priv->idle_load_view_id = g_idle_add ((GSourceFunc) idle_load_view, self); +} + +static void +load_new_editor (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + if ((priv->cached_editor == NULL) && (priv->idle_load_editor_id == 0)) + priv->idle_load_editor_id = g_idle_add ((GSourceFunc) idle_load_editor, self); } static void @@ -120,16 +201,27 @@ modest_window_mgr_init (ModestWindowMgr *obj) priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj); priv->window_list = NULL; + priv->banner_counter = 0; priv->main_window = NULL; priv->fullscreen_mode = FALSE; + priv->current_top = NULL; + priv->window_state_uids = NULL; + priv->modal_windows = g_queue_new (); + priv->queue_lock = g_mutex_new (); + priv->preregistered_uids = NULL; /* Could not initialize it from gconf, singletons are not ready yet */ - priv->show_toolbars = FALSE; - priv->show_toolbars_fullscreen = FALSE; - priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + priv->destroy_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + priv->viewer_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + + priv->closing_time = 0; + + priv->modal_handler_uids = NULL; + priv->cached_view = NULL; + priv->cached_editor = NULL; } static void @@ -137,13 +229,35 @@ modest_window_mgr_finalize (GObject *obj) { ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE(obj); + if (priv->idle_load_view_id > 0) { + g_source_remove (priv->idle_load_view_id); + priv->idle_load_view_id = 0; + } + + if (priv->idle_load_editor_id > 0) { + g_source_remove (priv->idle_load_editor_id); + priv->idle_load_editor_id = 0; + } + + if (priv->cached_view) { + gtk_widget_destroy (priv->cached_view); + priv->cached_view = NULL; + } + if (priv->cached_editor) { + gtk_widget_destroy (priv->cached_editor); + priv->cached_editor = NULL; + } + + modest_signal_mgr_disconnect_all_and_destroy (priv->window_state_uids); + priv->window_state_uids = NULL; + if (priv->window_list) { GList *iter = priv->window_list; /* unregister pending windows */ while (iter) { - modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), - MODEST_WINDOW (iter->data)); + ModestWindow *window = (ModestWindow *) iter->data; iter = g_list_next (iter); + modest_window_mgr_unregister_window (MODEST_WINDOW_MGR (obj), window); } g_list_free (priv->window_list); priv->window_list = NULL; @@ -159,9 +273,26 @@ modest_window_mgr_finalize (GObject *obj) priv->destroy_handlers = NULL; } + if (priv->viewer_handlers) { + g_hash_table_destroy (priv->viewer_handlers); + priv->viewer_handlers = NULL; + } + + modest_signal_mgr_disconnect_all_and_destroy (priv->modal_handler_uids); + priv->modal_handler_uids = NULL; + + if (priv->modal_windows) { + g_mutex_lock (priv->queue_lock); + g_queue_free (priv->modal_windows); + priv->modal_windows = NULL; + g_mutex_unlock (priv->queue_lock); + } + g_mutex_free (priv->queue_lock); + /* Do not unref priv->main_window because it does not hold a new reference */ + G_OBJECT_CLASS(parent_class)->finalize (obj); } @@ -199,7 +330,7 @@ remove_uid (GSList *list, const gchar *uid) GSList *cursor = list, *start = list; if (!uid) - return FALSE; + return list; while (cursor) { GSList *next = g_slist_next (cursor); @@ -222,7 +353,7 @@ append_uid (GSList *list, const gchar *uid) void -modest_window_mgr_register_header (ModestWindowMgr *self, TnyHeader *header) +modest_window_mgr_register_header (ModestWindowMgr *self, TnyHeader *header, const gchar *alt_uid) { ModestWindowMgrPrivate *priv; gchar* uid; @@ -232,10 +363,16 @@ modest_window_mgr_register_header (ModestWindowMgr *self, TnyHeader *header) priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); uid = modest_tny_folder_get_header_unique_id (header); + + if (uid == NULL) + uid = g_strdup (alt_uid); - if (!has_uid (priv->preregistered_uids, uid)) + if (!has_uid (priv->preregistered_uids, uid)) { + MODEST_DEBUG_BLOCK(g_debug ("registering new uid %s", uid);); priv->preregistered_uids = append_uid (priv->preregistered_uids, uid); - + } else + MODEST_DEBUG_BLOCK(g_debug ("already had uid %s", uid);); + g_free (uid); } @@ -250,24 +387,68 @@ modest_window_mgr_unregister_header (ModestWindowMgr *self, TnyHeader *header) priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); uid = modest_tny_folder_get_header_unique_id (header); + + if (!has_uid (priv->preregistered_uids, uid)) { + MODEST_DEBUG_BLOCK(g_debug ("trying to unregister non-existing uid %s", uid);); + priv->preregistered_uids = append_uid (priv->preregistered_uids, uid); + } else + MODEST_DEBUG_BLOCK(g_debug ("unregistering uid %s", uid);); - if (has_uid (priv->preregistered_uids, uid)) + if (has_uid (priv->preregistered_uids, uid)) { priv->preregistered_uids = remove_uid (priv->preregistered_uids, uid); - + if (has_uid (priv->preregistered_uids, uid)) + g_debug ("BUG: uid %s NOT removed", uid); + else + MODEST_DEBUG_BLOCK(g_debug ("uid %s removed", uid);); + } + g_free (uid); } + +#define MODEST_WINDOW_HELP_ID_PARAM "help-id" + +void +modest_window_mgr_register_help_id (ModestWindowMgr *self, GtkWindow *win, const gchar* help_id) +{ + /* we don't need 'self', but for API consistency... */ + g_return_if_fail (self && MODEST_IS_WINDOW_MGR(self)); + + g_return_if_fail (win && GTK_IS_WINDOW(win)); + g_return_if_fail (help_id); + + g_object_set_data_full (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM, + g_strdup(help_id), g_free); +} + + +const gchar* +modest_window_mgr_get_help_id (ModestWindowMgr *self, GtkWindow *win) +{ + /* we don't need 'self', but for API consistency... */ + g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR (self), NULL); + g_return_val_if_fail (win, NULL); + + return g_object_get_data (G_OBJECT(win), MODEST_WINDOW_HELP_ID_PARAM); +} + static gint compare_msguids (ModestWindow *win, const gchar *uid) { const gchar *msg_uid; - if (!MODEST_IS_MSG_VIEW_WINDOW (win)) + if ((!MODEST_IS_MSG_EDIT_WINDOW (win)) && (!MODEST_IS_MSG_VIEW_WINDOW (win))) return 1; /* Get message uid from msg window */ - msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win)); + if (MODEST_IS_MSG_EDIT_WINDOW (win)) { + msg_uid = modest_msg_edit_window_get_message_uid (MODEST_MSG_EDIT_WINDOW (win)); + if (msg_uid && uid &&!strcmp (msg_uid, uid)) + return 0; + } else { + msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (win)); + } if (msg_uid && uid &&!strcmp (msg_uid, uid)) return 0; @@ -275,15 +456,40 @@ compare_msguids (ModestWindow *win, return 1; } +void +modest_window_mgr_close_all_windows (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv = NULL; + gboolean ret_value = FALSE; + + g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + /* If there is a main window then try to close it, and it will + close the others if needed */ + if (priv->main_window) { + g_signal_emit_by_name (priv->main_window, "delete-event", NULL, &ret_value); + } else { + GList *wins = NULL, *next = NULL; + + /* delete-event handler actually removes window_list item, */ + wins = priv->window_list; + while (wins) { + next = g_list_next (wins); + g_signal_emit_by_name (G_OBJECT (wins->data), "delete-event", NULL, &ret_value); + wins = next; + } + } +} gboolean modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *header, ModestWindow **win) { - ModestWindowMgrPrivate *priv; - gchar* uid; - gboolean retval = FALSE; + ModestWindowMgrPrivate *priv = NULL; + gchar* uid = NULL; + gboolean has_header, has_window = FALSE; GList *item = NULL; g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE); @@ -293,106 +499,251 @@ modest_window_mgr_find_registered_header (ModestWindowMgr *self, TnyHeader *head uid = modest_tny_folder_get_header_unique_id (header); - /* first, look for the window */ - /* note, the UID cannot be in both the window list and the preregistered uid list */ - if (priv->window_list) { - item = g_list_find_custom (priv->window_list, - uid, (GCompareFunc) compare_msguids); - if (item) - retval = TRUE; - if (win) - *win = item ? MODEST_WINDOW(item->data) : NULL; + if (win) + *win = NULL; + + has_header = has_uid (priv->preregistered_uids, uid); + + item = g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids); + if (item) { + has_window = TRUE; + if (win) { + if ((!MODEST_IS_MSG_VIEW_WINDOW(item->data)) && + (!MODEST_IS_MSG_EDIT_WINDOW (item->data))) + g_debug ("not a valid window!"); + else { + g_debug ("found a window"); + *win = MODEST_WINDOW (item->data); + } + } } + g_free (uid); + return has_header || has_window; +} - /* IF It's not in the window list. maybe it's in our uid list... */ - retval = retval || has_uid (priv->preregistered_uids, uid); +static const gchar * +get_show_toolbar_key (GType window_type, + gboolean fullscreen) +{ + const gchar *key = NULL; + + if (window_type == MODEST_TYPE_MAIN_WINDOW) + key = (fullscreen) ? + MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR_FULLSCREEN : + MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR; + else if (window_type == MODEST_TYPE_MSG_VIEW_WINDOW) + key = (fullscreen) ? + MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN : + MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR; + else if (window_type == MODEST_TYPE_MSG_EDIT_WINDOW) + key = (fullscreen) ? + MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR_FULLSCREEN : + MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR; + else + g_return_val_if_reached (NULL); - g_free (uid); + return key; +} + +#ifndef MODEST_TOOLKIT_GTK +static void +on_window_is_topmost (GObject *gobject, + GParamSpec *arg1, + gpointer user_data) +{ + ModestWindowMgr *self; + ModestWindowMgrPrivate *priv; + ModestWindow *win = (ModestWindow *) gobject; - return retval; + g_return_if_fail (MODEST_IS_WINDOW_MGR (user_data)); + + self = MODEST_WINDOW_MGR (user_data); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + if (hildon_window_get_is_topmost (HILDON_WINDOW (win))) + priv->current_top = win; } +#else +static gboolean +on_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data) +{ + ModestWindowMgr *self; + ModestWindowMgrPrivate *priv; + + g_return_val_if_fail (MODEST_IS_WINDOW_MGR (user_data), FALSE); + self = MODEST_WINDOW_MGR (user_data); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + MODEST_DEBUG_BLOCK ( + if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN) + g_print ("GDK_WINDOW_STATE_WITHDRAWN\n"); + if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) + g_print ("GDK_WINDOW_STATE_ICONIFIED\n"); + if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) + g_print ("GDK_WINDOW_STATE_MAXIMIZED\n"); + if (event->changed_mask & GDK_WINDOW_STATE_STICKY) + g_print ("GDK_WINDOW_STATE_STICKY\n"); + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + g_print ("GDK_WINDOW_STATE_FULLSCREEN\n"); + if (event->changed_mask & GDK_WINDOW_STATE_ABOVE) + g_print ("GDK_WINDOW_STATE_ABOVE\n"); + if (event->changed_mask & GDK_WINDOW_STATE_BELOW) + g_print ("GDK_WINDOW_STATE_BELOW\n"); + ); + if (event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN || + event->changed_mask & GDK_WINDOW_STATE_ABOVE) { + priv->current_top = MODEST_WINDOW (widget); + } + + return FALSE; +} +#endif void modest_window_mgr_register_window (ModestWindowMgr *self, ModestWindow *window) { - static gboolean first_time = TRUE; GList *win; - gboolean show; ModestWindowMgrPrivate *priv; gint *handler_id; + const gchar *key; g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); - g_return_if_fail (MODEST_IS_WINDOW (window)); + g_return_if_fail (GTK_IS_WINDOW (window)); priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); win = g_list_find (priv->window_list, window); if (win) { - g_warning ("Trying to register an already registered window"); + /* this is for the case we want to register the window and it was already + * registered. We leave silently. + */ return; } - + /* Check that it's not a second main window */ if (MODEST_IS_MAIN_WINDOW (window)) { if (priv->main_window) { - g_warning ("Trying to register a second main window"); + g_warning ("%s: trying to register a second main window", + __FUNCTION__); return; } else { priv->main_window = window; + load_new_view (self); + load_new_editor (self); } } /* remove from the list of pre-registered uids */ if (MODEST_IS_MSG_VIEW_WINDOW(window)) { + const gchar *uid = modest_msg_view_window_get_message_uid + (MODEST_MSG_VIEW_WINDOW (window)); + + if (!has_uid (priv->preregistered_uids, uid)) + g_debug ("weird: no uid for window (%s)", uid); + + MODEST_DEBUG_BLOCK(g_debug ("registering window for %s", uid ? uid : "");); + priv->preregistered_uids = remove_uid (priv->preregistered_uids, modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window))); + } else if (MODEST_IS_MSG_EDIT_WINDOW(window)) { + const gchar *uid = modest_msg_edit_window_get_message_uid + (MODEST_MSG_EDIT_WINDOW (window)); + + MODEST_DEBUG_BLOCK(g_debug ("registering window for %s", uid);); + + priv->preregistered_uids = + remove_uid (priv->preregistered_uids, + modest_msg_edit_window_get_message_uid + (MODEST_MSG_EDIT_WINDOW (window))); } /* Add to list. Keep a reference to the window */ g_object_ref (window); priv->window_list = g_list_prepend (priv->window_list, window); + /* Listen to window state changes. Unfortunately + window-state-event does not properly work for the Maemo + version, so we need to use is-topmost and the ifdef */ +#ifndef MODEST_TOOLKIT_GTK + priv->window_state_uids = + modest_signal_mgr_connect (priv->window_state_uids, + G_OBJECT (window), + "notify::is-topmost", + G_CALLBACK (on_window_is_topmost), + self); +#else + priv->window_state_uids = + modest_signal_mgr_connect (priv->window_state_uids, + G_OBJECT (window), + "window-state-event", + G_CALLBACK (on_window_state_event), + self); +#endif /* Listen to object destruction */ handler_id = g_malloc0 (sizeof (gint)); *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self); -/* *handler_id = g_signal_connect (window, "destroy", G_CALLBACK (on_window_destroy), self); */ g_hash_table_insert (priv->destroy_handlers, window, handler_id); + /* If there is a msg view window, let the main window listen the msg-changed signal */ + if (MODEST_IS_MSG_VIEW_WINDOW(window) && priv->main_window) { + gulong *handler; + handler = g_malloc0 (sizeof (gulong)); + *handler = g_signal_connect (window, "msg-changed", + G_CALLBACK (modest_main_window_on_msg_view_window_msg_changed), + priv->main_window); + g_hash_table_insert (priv->viewer_handlers, window, handler); + } + /* Put into fullscreen if needed */ if (priv->fullscreen_mode) gtk_window_fullscreen (GTK_WINDOW (window)); - /* Fill caches */ - if (first_time) { - ModestConf *conf = modest_runtime_get_conf (); - priv->show_toolbars = - modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR, NULL); - priv->show_toolbars_fullscreen = - modest_conf_get_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, NULL); - first_time = FALSE; - } + /* Show/hide toolbar & fullscreen */ + key = get_show_toolbar_key (G_TYPE_FROM_INSTANCE (window), priv->fullscreen_mode); + modest_window_show_toolbar (window, modest_conf_get_bool (modest_runtime_get_conf (), key, NULL)); +} - /* Show/hide toolbar */ - if (priv->fullscreen_mode) - show = priv->show_toolbars_fullscreen; - else - show = priv->show_toolbars; - modest_window_show_toolbar (window, show); +static void +cancel_window_operations (ModestWindow *window) +{ + GSList* pending_ops = NULL; + + /* cancel open and receive operations */ + pending_ops = modest_mail_operation_queue_get_by_source (modest_runtime_get_mail_operation_queue (), + G_OBJECT (window)); + while (pending_ops != NULL) { + ModestMailOperationTypeOperation type; + GSList* tmp_list = NULL; + + type = modest_mail_operation_get_type_operation (MODEST_MAIL_OPERATION (pending_ops->data)); + if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || type == MODEST_MAIL_OPERATION_TYPE_OPEN) { + modest_mail_operation_cancel (pending_ops->data); + } + g_object_unref (G_OBJECT (pending_ops->data)); + tmp_list = pending_ops; + pending_ops = g_slist_next (pending_ops); + g_slist_free_1 (tmp_list); + } } -/* static void */ -/* on_window_destroy (ModestWindow *window, ModestWindowMgr *self) */ + + static gboolean on_window_destroy (ModestWindow *window, GdkEvent *event, ModestWindowMgr *self) { + gint dialog_response = GTK_RESPONSE_NONE; + gboolean no_propagate = FALSE; + /* Specific stuff first */ if (MODEST_IS_MAIN_WINDOW (window)) { ModestWindowMgrPrivate *priv; @@ -401,22 +752,47 @@ on_window_destroy (ModestWindow *window, /* If more than one window already opened */ if (g_list_length (priv->window_list) > 1) { + /* Present the window if it's not visible now */ + if (!gtk_window_has_toplevel_focus (GTK_WINDOW (window))) { + gtk_window_present (GTK_WINDOW (window)); + priv->current_top = window; + } + /* Create the confirmation dialog MSG-NOT308 */ + dialog_response = modest_platform_run_confirmation_dialog ( + GTK_WINDOW (window), _("emev_nc_close_windows")); + /* If the user wants to close all the windows */ - if (modest_main_window_close_all (MODEST_MAIN_WINDOW (window))) { - GList *iter = priv->window_list; - do { - if (iter->data != window) { - GList *tmp = iter->next; - on_window_destroy (MODEST_WINDOW (iter->data), - event, - self); - iter = tmp; - } else { - iter = g_list_next (iter); - } - } while (iter); + if ((dialog_response == GTK_RESPONSE_OK) + || (dialog_response == GTK_RESPONSE_ACCEPT) + || (dialog_response == GTK_RESPONSE_YES)) { + GList *iter = priv->window_list; + do { + if (iter->data != window) { + GList *tmp = iter->next; + on_window_destroy (MODEST_WINDOW (iter->data), + event, + self); + iter = tmp; + } else { + iter = g_list_next (iter); + } + } while (iter); + } else { + return TRUE; } } + + /* Do not unregister it, just hide */ + gtk_widget_hide_all (GTK_WIDGET (window)); + + /* Cancel pending operations */ + cancel_window_operations (window); + + /* Fake the window system, make it think that there is no window */ + if (modest_window_mgr_num_windows (self) == 0) + g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0); + + no_propagate = TRUE; } else { if (MODEST_IS_MSG_EDIT_WINDOW (window)) { @@ -425,27 +801,38 @@ on_window_destroy (ModestWindow *window, sent = modest_msg_edit_window_get_sent (MODEST_MSG_EDIT_WINDOW (window)); /* Save currently edited message to Drafts if it was not sent */ if (!sent && modest_msg_edit_window_is_modified (MODEST_MSG_EDIT_WINDOW (window))) { + + /* Raise the window if it's minimized */ + if (!gtk_window_has_toplevel_focus (GTK_WINDOW (window))) + gtk_window_present (GTK_WINDOW (window)); response = - modest_platform_run_yes_no_dialog (GTK_WINDOW (window), - _("mcen_nc_no_email_message_modified_save_changes")); + modest_platform_run_confirmation_dialog (GTK_WINDOW (window), + _("mcen_nc_no_email_message_modified_save_changes")); /* Save to drafts */ - if (response != GTK_RESPONSE_NO) - modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window)); - + if (response == GTK_RESPONSE_OK) + if (!modest_ui_actions_on_save_to_drafts (NULL, MODEST_MSG_EDIT_WINDOW (window))) + return TRUE; } } + /* Unregister window */ + modest_window_mgr_unregister_window (self, window); + no_propagate = TRUE; } - /* Save configuration state (TODO: why edit window does not require this function ?) */ - if (!MODEST_IS_MSG_EDIT_WINDOW (window)) - modest_window_save_state (MODEST_WINDOW(window)); - + return no_propagate; +} - /* Unregister window */ - modest_window_mgr_unregister_window (self, window); +static void +disconnect_msg_changed (gpointer key, + gpointer value, + gpointer user_data) +{ + guint handler_id; + handler_id = GPOINTER_TO_UINT(value); - return FALSE; + if (key && G_IS_OBJECT(key)) + g_signal_handler_disconnect (G_OBJECT (key), handler_id); } void @@ -454,7 +841,7 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self, { GList *win; ModestWindowMgrPrivate *priv; - gint *tmp, handler_id; + gulong *tmp, handler_id; g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); g_return_if_fail (MODEST_IS_WINDOW (window)); @@ -468,9 +855,33 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self, } /* If it's the main window unset it */ - if (priv->main_window == window) + if (priv->main_window == window) { priv->main_window = NULL; + /* Disconnect all emissions of msg-changed */ + if (priv->viewer_handlers) { + g_hash_table_foreach (priv->viewer_handlers, + disconnect_msg_changed, + NULL); + g_hash_table_destroy (priv->viewer_handlers); + priv->viewer_handlers = NULL; + } + } + + /* Remove the viewer window handler from the hash table. The + HashTable could not exist if the main window was closed + when there were other windows remaining */ + if (MODEST_IS_MSG_VIEW_WINDOW (window) && priv->viewer_handlers) { + tmp = (gulong *) g_hash_table_lookup (priv->viewer_handlers, window); + /* If the viewer was created without a main window + (for example when opening a message through D-Bus + the viewer handlers was not registered */ + if (tmp) { + g_signal_handler_disconnect (window, *tmp); + g_hash_table_remove (priv->viewer_handlers, window); + } + } + /* Save state */ modest_window_save_state (window); @@ -478,29 +889,51 @@ modest_window_mgr_unregister_window (ModestWindowMgr *self, priv->window_list = g_list_remove_link (priv->window_list, win); tmp = g_hash_table_lookup (priv->destroy_handlers, window); handler_id = *tmp; + g_hash_table_remove (priv->destroy_handlers, window); - /* Remove the reference to the window. Disconnect also the - delete-event handler, we won't need it anymore */ + /* cancel open and receive operations */ + cancel_window_operations (window); + + /* Check if it's the topmost window, and remove the window from the stack. + * This is needed for the cases there's no other topmost window that will + * replace it in topmost handler. + */ + if (window == priv->current_top) + priv->current_top = NULL; + + /* Disconnect the "window-state-event" handler, we won't need it anymore */ + if (priv->window_state_uids) { +#ifndef MODEST_TOOLKIT_GTK + priv->window_state_uids = + modest_signal_mgr_disconnect (priv->window_state_uids, + G_OBJECT (window), + "notify::is-topmost"); +#else + priv->window_state_uids = + modest_signal_mgr_disconnect (priv->window_state_uids, + G_OBJECT (window), + "window-state-event"); +#endif + } + + /* Disconnect the "delete-event" handler, we won't need it anymore */ g_signal_handler_disconnect (window, handler_id); - gtk_widget_destroy (win->data); - /* If there are no more windows registered then exit program */ - if (priv->window_list == NULL) { - ModestConf *conf = modest_runtime_get_conf (); + /* Disconnect all the window signals */ + modest_window_disconnect_signals (window); + + /* Destroy the window */ + g_object_unref (win->data); + gtk_widget_destroy (win->data); + g_list_free (win); + + /* If there are no more windows registered emit the signal */ + if (modest_window_mgr_num_windows (self) == 0) + g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0); +} - /* Save show toolbar status */ - modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR_FULLSCREEN, - priv->show_toolbars_fullscreen, NULL); - modest_conf_set_bool (conf, MODEST_CONF_SHOW_TOOLBAR, - priv->show_toolbars, NULL); - /* Quit main loop */ - /* FIXME: do we ever need to do this here? */ - if (gtk_main_level() > 0) - gtk_main_quit (); - } -} void modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self, @@ -508,6 +941,7 @@ modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self, { ModestWindowMgrPrivate *priv; GList *win = NULL; + ModestConf *conf; g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); @@ -519,18 +953,28 @@ modest_window_mgr_set_fullscreen_mode (ModestWindowMgr *self, priv->fullscreen_mode = on; + conf = modest_runtime_get_conf (); + /* Update windows */ win = priv->window_list; while (win) { - if (on) { + gboolean show; + const gchar *key = NULL; + + /* Getting this from gconf everytime is not that + expensive, we'll do it just a few times */ + key = get_show_toolbar_key (G_TYPE_FROM_INSTANCE (win->data), on); + show = modest_conf_get_bool (conf, key, NULL); + + /* Set fullscreen/unfullscreen */ + if (on) gtk_window_fullscreen (GTK_WINDOW (win->data)); - modest_window_show_toolbar (MODEST_WINDOW (win->data), - priv->show_toolbars_fullscreen); - } else { + else gtk_window_unfullscreen (GTK_WINDOW (win->data)); - modest_window_show_toolbar (MODEST_WINDOW (win->data), - priv->show_toolbars); - } + + /* Show/Hide toolbar */ + modest_window_show_toolbar (MODEST_WINDOW (win->data), show); + win = g_list_next (win); } } @@ -549,30 +993,27 @@ modest_window_mgr_get_fullscreen_mode (ModestWindowMgr *self) void modest_window_mgr_show_toolbars (ModestWindowMgr *self, + GType window_type, gboolean show_toolbars, gboolean fullscreen) { ModestWindowMgrPrivate *priv; + ModestConf *conf; + const gchar *key = NULL; g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + conf = modest_runtime_get_conf (); - /* If nothing changes then return. Otherwise cache it, do not - save to GConf for the moment, it will be done when all - windows become unregistered in order to avoid unnecessary - ModestConf calls */ - if (fullscreen) { - if (priv->show_toolbars_fullscreen == show_toolbars) - return; - else - priv->show_toolbars_fullscreen = show_toolbars; - } else { - if (priv->show_toolbars == show_toolbars) - return; - else - priv->show_toolbars = show_toolbars; - } + /* If nothing changes then return */ + key = get_show_toolbar_key (window_type, fullscreen); + conf = modest_runtime_get_conf (); + if (modest_conf_get_bool (conf, key, NULL) == show_toolbars) + return; + + /* Save in conf */ + modest_conf_set_bool (conf, key, show_toolbars, NULL); /* Apply now if the view mode is the right one */ if ((fullscreen && priv->fullscreen_mode) || @@ -581,25 +1022,128 @@ modest_window_mgr_show_toolbars (ModestWindowMgr *self, GList *win = priv->window_list; while (win) { - modest_window_show_toolbar (MODEST_WINDOW (win->data), - show_toolbars); + if (G_TYPE_FROM_INSTANCE (win->data) == window_type) + modest_window_show_toolbar (MODEST_WINDOW (win->data), + show_toolbars); win = g_list_next (win); } } } ModestWindow* -modest_window_mgr_get_main_window (ModestWindowMgr *self) +modest_window_mgr_get_main_window (ModestWindowMgr *self, gboolean create) +{ + ModestWindowMgrPrivate *priv; + + g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + /* create the main window, if it hasn't been created yet */ + if (!priv->main_window && create) { + /* modest_window_mgr_register_window will set priv->main_window */ + modest_window_mgr_register_window (self, modest_main_window_new ()); + MODEST_DEBUG_BLOCK( + g_debug ("%s: created main window: %p\n", __FUNCTION__, priv->main_window); + ); + } + + return priv->main_window; +} + + +gboolean +modest_window_mgr_main_window_exists (ModestWindowMgr *self) { ModestWindowMgrPrivate *priv; + + g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + return priv->main_window != NULL; +} + + +GtkWindow * +modest_window_mgr_get_modal (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv; + g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), NULL); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + return g_queue_peek_head (priv->modal_windows); +} + + +void +modest_window_mgr_set_modal (ModestWindowMgr *self, + GtkWindow *window) +{ + GtkWindow *old_modal; + ModestWindowMgrPrivate *priv; + + g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); + g_return_if_fail (GTK_IS_WINDOW (window)); priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + g_mutex_lock (priv->queue_lock); + old_modal = g_queue_peek_head (priv->modal_windows); + g_mutex_unlock (priv->queue_lock); + + if (!old_modal) { + /* make us transient wrt the main window then */ + if (priv->current_top && ((GtkWindow *) priv->current_top != window)) { + gtk_window_set_transient_for (window, GTK_WINDOW(priv->current_top)); + } else { + ModestWindow *main_win = modest_window_mgr_get_main_window (self, FALSE); + if (GTK_WINDOW(main_win) != window) /* they should not be the same */ + gtk_window_set_transient_for (window, GTK_WINDOW(main_win)); + } + gtk_window_set_modal (window, TRUE); + } else { + /* un-modalize the old one; the one on top should be the + * modal one */ + gtk_window_set_transient_for (window, GTK_WINDOW(old_modal)); + gtk_window_set_modal (window, TRUE); + } - return priv->main_window; + /* this will be the new modal window */ + g_mutex_lock (priv->queue_lock); + g_queue_push_head (priv->modal_windows, window); + g_mutex_unlock (priv->queue_lock); + + if (GTK_IS_DIALOG (window)) { + /* Note that response is not always enough because it + could be captured and removed easily by dialogs but + works for most of situations */ + priv->modal_handler_uids = + modest_signal_mgr_connect (priv->modal_handler_uids, + G_OBJECT (window), + "response", + G_CALLBACK (on_modal_dialog_close), + self); + /* We need this as well because dialogs are often + destroyed with gtk_widget_destroy and this one will + prevent response from happening */ + priv->modal_handler_uids = + modest_signal_mgr_connect (priv->modal_handler_uids, + G_OBJECT (window), + "destroy", + G_CALLBACK (on_modal_dialog_destroy), + self); + } else { + priv->modal_handler_uids = + modest_signal_mgr_connect (priv->modal_handler_uids, + G_OBJECT (window), + "delete-event", + G_CALLBACK (on_modal_window_close), + self); + } + /* Destroy width parent */ + gtk_window_set_destroy_with_parent (window, TRUE); } + static void on_nonhibernating_window_hide(GtkWidget *widget, gpointer user_data) { @@ -628,7 +1172,8 @@ on_nonhibernating_window_show(GtkWidget *widget, gpointer user_data) G_CALLBACK (on_nonhibernating_window_hide), self); } -void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self, +void +modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMgr *self, GtkWindow *window) { g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); @@ -642,7 +1187,8 @@ void modest_window_mgr_prevent_hibernation_while_window_is_shown (ModestWindowMg } } -gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self) +gboolean +modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self) { g_return_val_if_fail (MODEST_IS_WINDOW_MGR (self), FALSE); @@ -654,7 +1200,8 @@ gboolean modest_window_mgr_get_hibernation_is_prevented (ModestWindowMgr *self) } -void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self) +void +modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self) { g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); @@ -673,3 +1220,191 @@ void modest_window_mgr_save_state_for_all_windows (ModestWindowMgr *self) win = g_list_next (win); } } + +static gboolean +idle_top_modal (gpointer data) +{ + ModestWindowMgr *self = MODEST_WINDOW_MGR (data); + ModestWindowMgrPrivate *priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + GtkWindow *topmost; + + /* Get the top modal */ + g_mutex_lock (priv->queue_lock); + topmost = (GtkWindow *) g_queue_peek_head (priv->modal_windows); + g_mutex_unlock (priv->queue_lock); + + /* Show it */ + if (topmost) { + gdk_threads_enter (); + gtk_window_present (topmost); + /* It seems that the window looses modality if some + other is shown on top of it after set_transient_for + and set_parent */ + gtk_window_set_modal (topmost, TRUE); + gdk_threads_leave (); + } + + return FALSE; +} + +static void +remove_modal_from_queue (GtkWidget *widget, + ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv; + GList *item = NULL; + + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + /* Remove from queue. We don't use remove, because we want to + exit if the widget does not belong to the queue */ + g_mutex_lock (priv->queue_lock); + item = g_queue_find (priv->modal_windows, widget); + if (!item) { + g_warning ("Trying to remove a modal window that is not registered"); + g_mutex_unlock (priv->queue_lock); + return; + } + g_queue_unlink (priv->modal_windows, item); + g_mutex_unlock (priv->queue_lock); + + /* Disconnect handler */ + priv->modal_handler_uids = + modest_signal_mgr_disconnect (priv->modal_handler_uids, + G_OBJECT (widget), + GTK_IS_DIALOG (widget) ? + "response" : + "delete-event"); + if (GTK_IS_DIALOG (widget)) + priv->modal_handler_uids = + modest_signal_mgr_disconnect (priv->modal_handler_uids, + G_OBJECT (widget), + "destroy"); + + /* Schedule the next one for being shown */ + g_idle_add (idle_top_modal, self); +} + +static gboolean +on_modal_window_close (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data); + + /* Remove modal window from queue */ + remove_modal_from_queue (widget, self); + + /* Continue */ + return FALSE; +} + +static void +on_modal_dialog_close (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data); + + /* Remove modal window from queue. Note that if "destroy" + signal was invoked before the response the window could be + already deleted */ + remove_modal_from_queue (GTK_WIDGET (dialog), self); +} + +static void +on_modal_dialog_destroy (GtkObject *object, + gpointer user_data) +{ + ModestWindowMgr *self = MODEST_WINDOW_MGR (user_data); + + /* Remove modal window from queue */ + remove_modal_from_queue (GTK_WIDGET (object), self); +} + +gint +modest_window_mgr_num_windows (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv; + gint num_windows = 0; + + g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR(self), -1); + + priv = MODEST_WINDOW_MGR_GET_PRIVATE(self); + + if (priv->window_list) + num_windows = g_list_length (priv->window_list); + + /* Do not take into account the main window if it was hidden */ + if (priv->main_window && !GTK_WIDGET_VISIBLE (priv->main_window)) + num_windows--; + + return num_windows + priv->banner_counter; +} + +GtkWidget * +modest_window_mgr_get_msg_edit_window (ModestWindowMgr *self) +{ + GtkWidget *result; + ModestWindowMgrPrivate *priv; + + g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR(self), NULL); + + priv = MODEST_WINDOW_MGR_GET_PRIVATE(self); + + if (priv->cached_editor) { + result = priv->cached_editor; + priv->cached_editor = NULL; + load_new_editor (self); + } else { + result = g_object_new (MODEST_TYPE_MSG_EDIT_WINDOW, NULL); + } + + return result; +} + +GtkWidget * +modest_window_mgr_get_msg_view_window (ModestWindowMgr *self) +{ + GtkWidget *result; + ModestWindowMgrPrivate *priv; + + g_return_val_if_fail (self && MODEST_IS_WINDOW_MGR(self), NULL); + + priv = MODEST_WINDOW_MGR_GET_PRIVATE(self); + + if (priv->cached_view) { + result = priv->cached_view; + priv->cached_view = NULL; + load_new_view (self); + } else { + result = g_object_new (MODEST_TYPE_MSG_VIEW_WINDOW, NULL); + } + + return result; +} + +void +modest_window_mgr_register_banner (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv; + + g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + priv->banner_counter++; + +} + +void +modest_window_mgr_unregister_banner (ModestWindowMgr *self) +{ + ModestWindowMgrPrivate *priv; + + g_return_if_fail (MODEST_IS_WINDOW_MGR (self)); + priv = MODEST_WINDOW_MGR_GET_PRIVATE (self); + + priv->banner_counter--; + if (modest_window_mgr_num_windows (self) == 0) + g_signal_emit (self, signals[WINDOW_LIST_EMPTY_SIGNAL], 0); +}