Set better modal windows in hildon2 window manager to avoid keyboard focus
[modest] / src / hildon2 / modest-hildon2-window-mgr.c
index 4cef7e6..b5c6128 100644 (file)
@@ -35,6 +35,7 @@
 #include "modest-mailboxes-window.h"
 #include "modest-header-window.h"
 #include "modest-main-window.h"
+#include "modest-window-mgr-priv.h"
 #include "modest-conf.h"
 #include "modest-defs.h"
 #include "modest-signal-mgr.h"
@@ -48,7 +49,9 @@
 #include "modest-maemo-utils.h"
 #include "modest-utils.h"
 #include "modest-tny-msg.h"
+#include "modest-tny-account.h"
 #include <tny-merge-folder.h>
+#include <modest-account-mgr-helpers.h>
 
 /* 'private'/'protected' functions */
 static void modest_hildon2_window_mgr_class_init (ModestHildon2WindowMgrClass *klass);
@@ -84,11 +87,12 @@ static gboolean modest_hildon2_window_mgr_find_registered_message_uid (ModestWin
                                                                       ModestWindow **win);
 static GList *modest_hildon2_window_mgr_get_window_list (ModestWindowMgr *self);
 static gboolean modest_hildon2_window_mgr_close_all_windows (ModestWindowMgr *self);
-static gboolean window_can_close (ModestWindow *window);
+static gboolean modest_hildon2_window_mgr_close_all_but_initial (ModestWindowMgr *self);
 static gboolean window_has_modals (ModestWindow *window);
 static ModestWindow *modest_hildon2_window_mgr_show_initial_window (ModestWindowMgr *self);
 static ModestWindow *modest_hildon2_window_mgr_get_current_top (ModestWindowMgr *self);
 static gboolean modest_hildon2_window_mgr_screen_is_on (ModestWindowMgr *self);
+static void modest_hildon2_window_mgr_create_caches (ModestWindowMgr *self);
 static void osso_display_event_cb (osso_display_state_t state, 
                                   gpointer data);
 static void on_account_removed (TnyAccountStore *acc_store, 
@@ -170,9 +174,11 @@ modest_hildon2_window_mgr_class_init (ModestHildon2WindowMgrClass *klass)
        mgr_class->find_registered_message_uid = modest_hildon2_window_mgr_find_registered_message_uid;
        mgr_class->get_window_list = modest_hildon2_window_mgr_get_window_list;
        mgr_class->close_all_windows = modest_hildon2_window_mgr_close_all_windows;
+       mgr_class->close_all_but_initial = modest_hildon2_window_mgr_close_all_but_initial;
        mgr_class->show_initial_window = modest_hildon2_window_mgr_show_initial_window;
        mgr_class->get_current_top = modest_hildon2_window_mgr_get_current_top;
        mgr_class->screen_is_on = modest_hildon2_window_mgr_screen_is_on;
+       mgr_class->create_caches = modest_hildon2_window_mgr_create_caches;
 
        g_type_class_add_private (gobject_class, sizeof(ModestHildon2WindowMgrPrivate));
 
@@ -431,12 +437,18 @@ modest_hildon2_window_mgr_register_window (ModestWindowMgr *self,
        HildonWindowStack *stack;
        gboolean nested_msg = FALSE;
        ModestWindow *current_top;
+       const gchar *acc_name;
 
        g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), FALSE);
        g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
 
        priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
 
+       /* Try to close active modal dialogs */
+       if (modest_window_mgr_get_num_windows (self) &&
+           !_modest_window_mgr_close_active_modals (self))
+               return FALSE;
+
        stack = hildon_window_stack_get_default ();
        current_top = (ModestWindow *) hildon_window_stack_peek (stack);
 
@@ -448,41 +460,67 @@ modest_hildon2_window_mgr_register_window (ModestWindowMgr *self,
                return FALSE;
        }
 
+       /* Do not allow standalone editors or standalone viewers */
+       if (!current_top &&
+           (MODEST_IS_MSG_VIEW_WINDOW (window) ||
+            MODEST_IS_MSG_EDIT_WINDOW (window)))
+               modest_window_mgr_show_initial_window (self);
+
        if (MODEST_IS_MSG_VIEW_WINDOW (window)) {
                gchar *uid;
                TnyHeader *header;
+
+               uid = g_strdup (modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window)));
+               
                header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (window));
 
-               if (header) {
+               if (uid == NULL)
                        uid = modest_tny_folder_get_header_unique_id (header);
-
-                       /* Embedded messages do not have uid */
-                       if (uid) {
-                               if (g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids)) {
-                                       g_debug ("%s found another view window showing the same header", __FUNCTION__);
-                                       g_free (uid);
-                                       g_object_unref (header);
-                                       return FALSE;
-                               }
+               /* Embedded messages do not have uid */
+               if (uid) {
+                       if (g_list_find_custom (priv->window_list, uid, (GCompareFunc) compare_msguids)) {
+                               g_debug ("%s found another view window showing the same header", __FUNCTION__);
                                g_free (uid);
-                       } else {
-                               if (g_list_find_custom (priv->window_list, header, (GCompareFunc) compare_headers)) {
-                                       g_debug ("%s found another view window showing the same header", __FUNCTION__);
-                                       g_object_unref (header);
-                                       return FALSE;
-                               }
+                               g_object_unref (header);
+                               return FALSE;
+                       }
+                       g_free (uid);
+               } else if (header) {
+                       if (g_list_find_custom (priv->window_list, header, (GCompareFunc) compare_headers)) {
+                               g_debug ("%s found another view window showing the same header", __FUNCTION__);
+                               g_object_unref (header);
+                               return FALSE;
                        }
-                       g_object_unref (header);
                }
+               if (header)
+                       g_object_unref (header);
        }
 
-       if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_FOLDER_WINDOW (window)) {
-               gtk_window_present (GTK_WINDOW (window));
+       /* Do not go backwards */
+       if ((MODEST_IS_MSG_VIEW_WINDOW (current_top) ||
+            MODEST_IS_MSG_EDIT_WINDOW (current_top) ||
+            MODEST_IS_HEADER_WINDOW (current_top)) &&
+           (MODEST_IS_FOLDER_WINDOW (window) ||
+            MODEST_IS_ACCOUNTS_WINDOW (window) ||
+            MODEST_IS_MAILBOXES_WINDOW (window))) {
+               gtk_window_present (GTK_WINDOW (current_top));
                return FALSE;
        }
 
+       if (MODEST_IS_FOLDER_WINDOW (current_top) && MODEST_IS_FOLDER_WINDOW (window)) {
+               gboolean retval;
+
+               g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
+
+               if (retval) {
+                       gtk_window_present (GTK_WINDOW (current_top));
+                       return FALSE;
+               }
+               current_top = (ModestWindow *) hildon_window_stack_peek (stack);
+       }
+
        if (MODEST_IS_MAILBOXES_WINDOW (current_top) && MODEST_IS_MAILBOXES_WINDOW (window)) {
-               gtk_window_present (GTK_WINDOW (window));
+               gtk_window_present (GTK_WINDOW (current_top));
                return FALSE;
        }
 
@@ -534,7 +572,7 @@ modest_hildon2_window_mgr_register_window (ModestWindowMgr *self,
 
                /* Close the current view */
                g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
-               if (retval == TRUE) {
+               if (retval) {
                        /* Cancelled closing top window, then we fail to register */
                        goto fail;
                }
@@ -545,8 +583,26 @@ modest_hildon2_window_mgr_register_window (ModestWindowMgr *self,
        *handler_id = g_signal_connect (window, "delete-event", G_CALLBACK (on_window_destroy), self);
        g_hash_table_insert (priv->destroy_handlers, window, handler_id);
 
+       if (!MODEST_IS_MSG_EDIT_WINDOW (window) &&
+           !MODEST_IS_ACCOUNTS_WINDOW (window)) {
+               acc_name = modest_window_get_active_account (window);
+
+               if (acc_name) {
+                       modest_platform_remove_new_mail_notifications (FALSE, acc_name);
+                       modest_account_mgr_set_has_new_mails (modest_runtime_get_account_mgr (),
+                                                             acc_name,
+                                                             FALSE);
+               }
+       }
+
        /* Show toolbar always */
-       modest_window_show_toolbar (window, TRUE);
+       if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
+               gboolean show_toolbar;
+               show_toolbar = modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR, NULL);
+               modest_window_show_toolbar (window, show_toolbar);
+       } else {
+               modest_window_show_toolbar (window, TRUE);
+       }
 
        return TRUE;
 fail:
@@ -572,7 +628,9 @@ cancel_window_operations (ModestWindow *window)
                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) {
+               if (type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
+                   type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
+                   type == MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE) {
                        modest_mail_operation_cancel (pending_ops->data);
                }
                g_object_unref (G_OBJECT (pending_ops->data));
@@ -604,24 +662,14 @@ window_has_modals (ModestWindow *window)
 }
 
 static gboolean
-window_can_close (ModestWindow *window)
-{
-       /* An editor can be always closed no matter the dialogs it has 
-        * on top. */
-       if (MODEST_IS_MSG_EDIT_WINDOW (window))
-               return TRUE;
-
-       return !window_has_modals (window);
-}
-
-static gboolean
 on_window_destroy (ModestWindow *window, 
                   GdkEvent *event,
                   ModestHildon2WindowMgr *self)
 {
        gboolean no_propagate = FALSE;
 
-       if (!window_can_close (window))
+       /* Do not close the window if it has modals on top */
+       if (!MODEST_IS_MSG_EDIT_WINDOW (window) && window_has_modals (window))
                return TRUE;
 
        if (MODEST_IS_MSG_EDIT_WINDOW (window)) {
@@ -689,7 +737,7 @@ modest_hildon2_window_mgr_unregister_window (ModestWindowMgr *self,
 
        win = g_list_find (priv->window_list, window);
        if (!win) {
-               g_warning ("Trying to unregister a window that has not being registered yet");
+               g_debug ("Trying to unregister a window that has not being registered yet");
                return;
        }
 
@@ -803,32 +851,60 @@ modest_hildon2_window_mgr_get_main_window (ModestWindowMgr *self, gboolean show)
                gtk_widget_show_all (GTK_WIDGET (result));
                gtk_window_present (GTK_WINDOW (result));
        }
-       
+
        return result;
 }
 
+static gint
+look_for_transient (gconstpointer a,
+                   gconstpointer b)
+{
+       GtkWindow *win, *child;
+
+       if (a == b)
+               return 1;
+
+       child = (GtkWindow *) b;
+       win = (GtkWindow *) a;
+
+       if ((gtk_window_get_transient_for (win) == child) &&
+           GTK_WIDGET_VISIBLE (win))
+               return 0;
+       else
+               return 1;
+}
 
 static GtkWindow *
 modest_hildon2_window_mgr_get_modal (ModestWindowMgr *self)
 {
        ModestHildon2WindowMgrPrivate *priv;
        GList *toplevel_list;
-       GtkWidget *toplevel;
-       
+       GtkWidget *current_top, *toplevel;
+       HildonWindowStack *stack;
+
        g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self), NULL);
        priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (self);
 
-       toplevel = NULL;
+       /* Get current top */
+       stack = hildon_window_stack_get_default ();
+       current_top = hildon_window_stack_peek (stack);
+       toplevel = current_top;
        toplevel_list = gtk_window_list_toplevels ();
-       while (toplevel_list) {
-               if (gtk_window_is_active (toplevel_list->data)) {
-                       toplevel = toplevel_list->data;
+
+       while (toplevel) {
+               GList *parent_link;
+
+               parent_link = g_list_find_custom (toplevel_list, toplevel, look_for_transient);
+               if (parent_link)
+                       toplevel = (GtkWidget *) parent_link->data;
+               else
                        break;
-               }
-               toplevel_list = g_list_next (toplevel_list);
        }
 
-       return NULL;
+       if (toplevel && GTK_WIDGET_VISIBLE (toplevel) && gtk_window_get_modal ((GtkWindow *) toplevel))
+               return (GtkWindow *) toplevel;
+       else
+               return NULL;
 }
 
 
@@ -843,16 +919,60 @@ modest_hildon2_window_mgr_set_modal (ModestWindowMgr *self,
        gtk_window_set_modal (window, TRUE);
        gtk_window_set_transient_for (window, parent);
        gtk_window_set_destroy_with_parent (window, TRUE);
+
+       if (!gtk_window_has_toplevel_focus (window)) {
+               GList *toplevels, *node;
+
+               toplevels = gtk_window_list_toplevels ();
+               for (node = toplevels; node != NULL; node = g_list_next (node)) {
+                       if (gtk_window_has_toplevel_focus (GTK_WINDOW (node->data))) {
+                               if (GTK_IS_DIALOG (node->data)) {                                       
+                                       gtk_window_set_transient_for (window, GTK_WINDOW (node->data));
+                               }
+                               break;
+                       }
+               }
+       }
 }
 
 static void
-on_account_removed (TnyAccountStore *acc_store, 
+close_all_but_first (gpointer data)
+{
+       gint num_windows, i;
+       gboolean retval;
+       HildonWindowStack *stack;
+
+       stack = hildon_window_stack_get_default ();
+       g_return_if_fail (stack);
+
+       num_windows = hildon_window_stack_size (stack);
+
+       for (i = 0; i < (num_windows - 1); i++) {
+               GtkWidget *current_top;
+
+               /* Close window */
+               current_top = hildon_window_stack_peek (stack);
+               g_signal_emit_by_name (G_OBJECT (current_top), "delete-event", NULL, &retval);
+       }
+}
+
+static gboolean
+on_idle_close_all_but_first (gpointer data)
+{
+       gdk_threads_enter ();
+       close_all_but_first (data);
+       gdk_threads_leave ();
+
+       return FALSE;
+}
+
+static void
+on_account_removed (TnyAccountStore *acc_store,
                    TnyAccount *account,
                    gpointer user_data)
 {
        HildonWindowStack *stack;
        ModestWindow *current_top;
-       gboolean has_accounts;
 
        /* Ignore transport account removals */
        if (TNY_IS_TRANSPORT_ACCOUNT (account))
@@ -860,52 +980,24 @@ on_account_removed (TnyAccountStore *acc_store,
 
        stack = hildon_window_stack_get_default ();
        current_top = (ModestWindow *) hildon_window_stack_peek (stack);
-       has_accounts = modest_account_mgr_has_accounts (modest_runtime_get_account_mgr (), TRUE);
 
        /* if we're showing the header view of the currently deleted
           account, or the outbox and we deleted the last account,
           then close the window */
-       if (current_top && MODEST_IS_HEADER_WINDOW (current_top)) {
-               ModestHeaderView *header_view;
-               TnyFolder *folder;
-               gboolean deleted = FALSE;
-
-               header_view = modest_header_window_get_header_view (MODEST_HEADER_WINDOW (current_top));
-               folder = modest_header_view_get_folder (header_view);
-               if (folder) {
-                       gboolean retval;
-
-                       /* Close if it's my account */
-                       if (!TNY_IS_MERGE_FOLDER (folder)) {
-                               TnyAccount *my_account;
-
-                               my_account = tny_folder_get_account (folder);
-                               if (my_account) {
-                                       if (my_account == account) {
-                                               g_signal_emit_by_name (G_OBJECT (current_top),
-                                                                      "delete-event",
-                                                                      NULL, &retval);
-                                               deleted = TRUE;
-                                       }
-                                       g_object_unref (my_account);
-                               }
-                       }
-
-                       /* Close if viewing outbox and no account left */
-                       if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_OUTBOX) {
-                               if (!has_accounts) {
-                                       g_signal_emit_by_name (G_OBJECT (current_top),
-                                                              "delete-event",
-                                                              NULL, &retval);
-                                       deleted = TRUE;
-                               }
-                       }
-                       g_object_unref (folder);
-
-                       if (deleted) {
-                               current_top = (ModestWindow *) hildon_window_stack_peek (stack);
-                       }
-               }
+       if (current_top &&
+           (MODEST_IS_HEADER_WINDOW (current_top) ||
+            MODEST_IS_FOLDER_WINDOW (current_top))) {
+                   const gchar *acc_name;
+
+                   acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
+
+                   /* We emit it in an idle, because sometimes this
+                      function could called when the account settings
+                      dialog is about to close but still there. That
+                      modal dialog would otherwise, prevent the
+                      windows from being closed */
+                   if (!strcmp (acc_name, modest_window_get_active_account (current_top)))
+                           g_idle_add (on_idle_close_all_but_first, NULL);
        }
 }
 
@@ -980,8 +1072,18 @@ modest_hildon2_window_mgr_screen_is_on (ModestWindowMgr *self)
        return (priv->display_state == OSSO_DISPLAY_ON) ? TRUE : FALSE;
 }
 
-static void 
-osso_display_event_cb (osso_display_state_t state, 
+static void
+modest_hildon2_window_mgr_create_caches (ModestWindowMgr *self)
+{
+       g_return_if_fail (MODEST_IS_HILDON2_WINDOW_MGR (self));
+
+       modest_accounts_window_pre_create ();
+
+       MODEST_WINDOW_MGR_CLASS(parent_class)->create_caches (self);
+}
+
+static void
+osso_display_event_cb (osso_display_state_t state,
                       gpointer data)
 {
        ModestHildon2WindowMgrPrivate *priv = MODEST_HILDON2_WINDOW_MGR_GET_PRIVATE (data);
@@ -990,6 +1092,37 @@ osso_display_event_cb (osso_display_state_t state,
 
        /* Stop blinking if the screen becomes on */
        if (priv->display_state == OSSO_DISPLAY_ON)
-               modest_platform_remove_new_mail_notifications (TRUE);
+               modest_platform_remove_new_mail_notifications (TRUE, NULL);
 }
 
+static gboolean
+modest_hildon2_window_mgr_close_all_but_initial (ModestWindowMgr *self)
+{
+       ModestWindow *top;
+
+       /* Exit if there are no windows */
+       if (!modest_window_mgr_get_num_windows (self)) {
+               g_warning ("%s: unable to close, there are no windows", __FUNCTION__);
+               return FALSE;
+       }
+
+       /* Close active modals */
+       if (!_modest_window_mgr_close_active_modals (self)) {
+               g_debug ("%s: unable to close some dialogs", __FUNCTION__);
+               return FALSE;
+       }
+
+       /* Close all but first */
+       top = modest_window_mgr_get_current_top (self);
+       if (!MODEST_IS_ACCOUNTS_WINDOW (top))
+               close_all_but_first (NULL);
+
+       /* If some cannot be closed return */
+       top = modest_window_mgr_get_current_top (self);
+       if (!MODEST_IS_ACCOUNTS_WINDOW (top)) {
+               g_debug ("%s: could not close some windows", __FUNCTION__);
+               return FALSE;
+       }
+
+       return TRUE;
+}