summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2021-05-26 22:11:31 +0200
committerMilan Crha <mcrha@redhat.com>2021-05-26 22:12:14 +0200
commit686b9264f1df86eda95ecc76d0bd2167e123be66 (patch)
tree56960a4767d5d28047b63cb8b61f0e9b69e9dc2a
parent498c0f8f1f7eaa9422e015e43e4d9c0f3dcd26e2 (diff)
downloadevolution-686b9264f1df86eda95ecc76d0bd2167e123be66.tar.gz
I#1507 - Contacts: Preserve selection after search change
Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1507
-rw-r--r--src/addressbook/gui/widgets/e-addressbook-model.c21
-rw-r--r--src/addressbook/gui/widgets/e-addressbook-view.c110
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",