[evolution] I#1507 - Contacts: Preserve selection after search change



commit be4acce19144aa4187f15097f564dd75b40618d8
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 26 22:11:31 2021 +0200

    I#1507 - Contacts: Preserve selection after search change
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1507

 src/addressbook/gui/widgets/e-addressbook-model.c |  21 +++--
 src/addressbook/gui/widgets/e-addressbook-view.c  | 110 ++++++++++++++++++++++
 2 files changed, 125 insertions(+), 6 deletions(-)
---
diff --git a/src/addressbook/gui/widgets/e-addressbook-model.c 
b/src/addressbook/gui/widgets/e-addressbook-model.c
index 3b83cd16b2..49d0d33b49 100644
--- a/src/addressbook/gui/widgets/e-addressbook-model.c
+++ b/src/addressbook/gui/widgets/e-addressbook-model.c
@@ -68,6 +68,7 @@ enum {
 enum {
        WRITABLE_STATUS,
        STATUS_MESSAGE,
+       BEFORE_SEARCH,
        SEARCH_STARTED,
        SEARCH_RESULT,
        FOLDER_BAR_MESSAGE,
@@ -372,6 +373,8 @@ client_view_ready_cb (GObject *source_object,
                return;
        }
 
+       g_signal_emit (model, signals[BEFORE_SEARCH], 0);
+
        remove_book_view (model);
        free_data (model);
 
@@ -681,6 +684,15 @@ e_addressbook_model_class_init (EAddressbookModelClass *class)
                G_TYPE_STRING,
                G_TYPE_INT);
 
+       signals[BEFORE_SEARCH] = g_signal_new (
+               "before-search",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_LAST,
+               /* G_STRUCT_OFFSET (EAddressbookModelClass, before_search) */ 0,
+               NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0);
+
        signals[SEARCH_STARTED] = g_signal_new (
                "search_started",
                G_OBJECT_CLASS_TYPE (object_class),
@@ -851,6 +863,7 @@ e_addressbook_model_contact_at (EAddressbookModel *model,
                                 gint index)
 {
        g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
+       g_return_val_if_fail (index >= 0 && (guint) index < model->priv->contacts->len, NULL);
 
        return model->priv->contacts->pdata[index];
 }
@@ -862,11 +875,6 @@ e_addressbook_model_find (EAddressbookModel *model,
        GPtrArray *array;
        gint ii;
 
-       /* XXX This searches for a particular EContact instance,
-        *     as opposed to an equivalent but possibly different
-        *     EContact instance.  Might have to revise this in
-        *     the future. */
-
        g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), -1);
        g_return_val_if_fail (E_IS_CONTACT (contact), -1);
 
@@ -874,7 +882,8 @@ e_addressbook_model_find (EAddressbookModel *model,
        for (ii = 0; ii < array->len; ii++) {
                EContact *candidate = array->pdata[ii];
 
-               if (contact == candidate)
+               if (contact == candidate ||
+                   g_strcmp0 (e_contact_get_const (contact, E_CONTACT_UID), e_contact_get_const (candidate, 
E_CONTACT_UID)) == 0)
                        return ii;
        }
 
diff --git a/src/addressbook/gui/widgets/e-addressbook-view.c 
b/src/addressbook/gui/widgets/e-addressbook-view.c
index 0b6d8827d6..b4c4ff199b 100644
--- a/src/addressbook/gui/widgets/e-addressbook-view.c
+++ b/src/addressbook/gui/widgets/e-addressbook-view.c
@@ -78,6 +78,11 @@ struct _EAddressbookViewPrivate {
 
        GtkTargetList *copy_target_list;
        GtkTargetList *paste_target_list;
+
+       GSList *previous_selection; /* EContact * */
+       EContact *cursor_contact;
+       gint cursor_col;
+       gboolean awaiting_search_start;
 };
 
 enum {
@@ -145,6 +150,14 @@ addressbook_view_emit_popup_event (EAddressbookView *view,
 static void
 addressbook_view_emit_selection_change (EAddressbookView *view)
 {
+       if (!view->priv->awaiting_search_start &&
+           e_selection_model_selected_count (e_addressbook_view_get_selection_model (view)) > 0) {
+               g_slist_free_full (view->priv->previous_selection, g_object_unref);
+               view->priv->previous_selection = NULL;
+
+               g_clear_object (&view->priv->cursor_contact);
+       }
+
        g_signal_emit (view, signals[SELECTION_CHANGE], 0);
 }
 
@@ -429,6 +442,89 @@ addressbook_view_display_view_cb (GalViewInstance *view_instance,
        command_state_change (view);
 }
 
+static void
+addressbook_view_model_before_search_cb (EAddressbookModel *model,
+                                        gpointer user_data)
+{
+       EAddressbookView *view = user_data;
+       ESelectionModel *selection_model;
+       gint cursor_row;
+
+       selection_model = e_addressbook_view_get_selection_model (view);
+
+       g_slist_free_full (view->priv->previous_selection, g_object_unref);
+       view->priv->previous_selection = e_addressbook_view_get_selected (view);
+
+       g_clear_object (&view->priv->cursor_contact);
+
+       cursor_row = e_selection_model_cursor_row (selection_model);
+
+       if (cursor_row >= 0 && cursor_row < e_addressbook_model_contact_count (model))
+               view->priv->cursor_contact = g_object_ref (e_addressbook_model_contact_at (model, 
cursor_row));
+
+       view->priv->cursor_col = e_selection_model_cursor_col (selection_model);
+       view->priv->awaiting_search_start = TRUE;
+}
+
+static void
+addressbook_view_model_search_started_cb (EAddressbookModel *model,
+                                         gpointer user_data)
+{
+       EAddressbookView *view = user_data;
+
+       view->priv->awaiting_search_start = FALSE;
+}
+
+static void
+addressbook_view_model_search_result_cb (EAddressbookModel *model,
+                                        const GError *error,
+                                        gpointer user_data)
+{
+       EAddressbookView *view = user_data;
+       ESelectionModel *selection_model;
+       EContact *cursor_contact;
+       GSList *previous_selection, *link;
+       gint row;
+
+       view->priv->awaiting_search_start = FALSE;
+
+       if (!view->priv->previous_selection && !view->priv->cursor_contact)
+               return;
+
+       /* This can change selection, which frees the 'previous_selection', thus take
+          ownership of it. */
+       previous_selection = view->priv->previous_selection;
+       view->priv->previous_selection = NULL;
+
+       cursor_contact = view->priv->cursor_contact;
+       view->priv->cursor_contact = NULL;
+
+       selection_model = e_addressbook_view_get_selection_model (view);
+
+       if (cursor_contact) {
+               row = e_addressbook_model_find (model, cursor_contact);
+
+               if (row >= 0) {
+                       e_selection_model_change_cursor (selection_model, row, view->priv->cursor_col);
+                       e_selection_model_cursor_changed (selection_model, row, view->priv->cursor_col);
+               }
+       }
+
+       for (link = previous_selection; link; link = g_slist_next (link)) {
+               EContact *contact = link->data;
+
+               row = e_addressbook_model_find (model, contact);
+
+               if (row >= 0)
+                       e_selection_model_change_one_row (selection_model, row, TRUE);
+       }
+
+       g_slist_free_full (previous_selection, g_object_unref);
+       g_clear_object (&cursor_contact);
+
+       e_selection_model_selection_changed (selection_model);
+}
+
 static gboolean
 address_book_view_focus_in_cb (EAddressbookView *view,
                               GdkEvent *event)
@@ -575,6 +671,11 @@ addressbook_view_dispose (GObject *object)
        g_clear_pointer (&priv->copy_target_list, gtk_target_list_unref);
        g_clear_pointer (&priv->paste_target_list, gtk_target_list_unref);
 
+       g_slist_free_full (priv->previous_selection, g_object_unref);
+       priv->previous_selection = NULL;
+
+       g_clear_object (&priv->cursor_contact);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_addressbook_view_parent_class)->dispose (object);
 }
@@ -601,6 +702,15 @@ addressbook_view_constructed (GObject *object)
 
        view->priv->model = e_addressbook_model_new (client_cache);
 
+       g_signal_connect_object (view->priv->model, "before-search",
+               G_CALLBACK (addressbook_view_model_before_search_cb), view, 0);
+
+       g_signal_connect_object (view->priv->model, "search-started",
+               G_CALLBACK (addressbook_view_model_search_started_cb), view, 0);
+
+       g_signal_connect_object (view->priv->model, "search-result",
+               G_CALLBACK (addressbook_view_model_search_result_cb), view, 0);
+
        view_instance = e_shell_view_new_view_instance (shell_view, uid);
        g_signal_connect (
                view_instance, "display-view",


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]