[gnome-contacts/nielsdg/filters-and-sorters: 2/4] Add a GtkFilter and GtkSorter for Personas




commit 83aa3cebdcdc22591fd4c693630ce2cf6bd525a5
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Wed Aug 17 00:31:07 2022 +0200

    Add a GtkFilter and GtkSorter for Personas
    
    This allows us to split off the logic for filtering and sorting personas
    from the generic `Utils` namespace, and allows us to expand on our
    GListModel usage, which allows for some bigger cleanups planned ahead.

 src/contacts-contact-sheet.vala          | 14 ++---
 src/contacts-linked-personas-dialog.vala | 87 +++++++++++++++++---------------
 src/contacts-persona-filter.vala         | 52 +++++++++++++++++++
 src/contacts-persona-sorter.vala         | 57 +++++++++++++++++++++
 src/contacts-utils.vala                  | 38 ++------------
 src/meson.build                          |  2 +
 6 files changed, 170 insertions(+), 80 deletions(-)
---
diff --git a/src/contacts-contact-sheet.vala b/src/contacts-contact-sheet.vala
index b6e71bb5..1b0eecff 100644
--- a/src/contacts-contact-sheet.vala
+++ b/src/contacts-contact-sheet.vala
@@ -133,19 +133,21 @@ public class Contacts.ContactSheet : Gtk.Grid {
 
     this.last_row++;
 
-    var personas = Utils.get_personas_for_display (this.individual);
-    /* Cause personas are sorted properly I can do this */
-    for (int i = 0; i < personas.get_n_items (); i++) {
-      var p = (Persona) personas.get_item (i);
+    var personas = Utils.personas_as_list_model (individual);
+    var personas_filtered = new Gtk.FilterListModel (personas, new PersonaFilter ());
+    var personas_sorted = new Gtk.SortListModel (personas_filtered, new PersonaSorter ());
+
+    for (int i = 0; i < personas_sorted.get_n_items (); i++) {
+      var persona = (Persona) personas_sorted.get_item (i);
       int persona_store_pos = this.last_row;
 
       if (i > 0) {
-        this.attach (create_persona_store_label (p), 0, this.last_row, 3);
+        this.attach (create_persona_store_label (persona), 0, this.last_row, 3);
         this.last_row++;
       }
 
       foreach (unowned var prop in SORTED_PROPERTIES)
-        add_row_for_property (p, prop);
+        add_row_for_property (persona, prop);
 
       // Nothing to show in the persona: don't mention it
       bool is_empty_persona = (this.last_row == persona_store_pos + 1);
diff --git a/src/contacts-linked-personas-dialog.vala b/src/contacts-linked-personas-dialog.vala
index b66e94ce..7d870951 100644
--- a/src/contacts-linked-personas-dialog.vala
+++ b/src/contacts-linked-personas-dialog.vala
@@ -39,46 +39,51 @@ public class Contacts.LinkedPersonasDialog : Gtk.Dialog {
     this.linked_accounts_view.set_header_func (add_separator);
 
     // loading personas for display
-    var personas = Contacts.Utils.get_personas_for_display (individual);
-    for (int i = 1; i < personas.get_n_items (); i++) {
-      var p = (Persona) personas.get_item (i);
-      var row_grid = new Gtk.Grid ();
-
-      var image_frame = new Avatar (AVATAR_SIZE, individual);
-      image_frame.set_hexpand (false);
-      image_frame.margin_top = 6;
-      image_frame.margin_bottom = 6;
-      image_frame.margin_start = 6;
-      image_frame.margin_end = 12;
-      row_grid.attach (image_frame, 0, 0, 1, 2);
-
-      var display_name = new Gtk.Label ("");
-      display_name.set_halign (Gtk.Align.START);
-      display_name.set_valign (Gtk.Align.END);
-      display_name.set_hexpand (true);
-      display_name.set_markup (Markup.printf_escaped ("<span font='bold'>%s</span>", p.display_id));
-
-      row_grid.attach (display_name, 1, 0, 1, 1);
-
-      var store_name = new Gtk.Label (Contacts.Utils.format_persona_store_name_for_contact (p));
-      store_name.set_halign (Gtk.Align.START);
-      store_name.set_valign (Gtk.Align.START);
-      store_name.set_hexpand (true);
-      store_name.get_style_context ().add_class ("dim-label");
-      row_grid.attach (store_name, 1, 1, 1, 1);
-
-      var button = new Gtk.Button.with_label (_("Unlink"));
-      button.margin_end = 6;
-      button.set_valign (Gtk.Align.CENTER);
-      // button.get_child ().margin = 1; XXX
-      row_grid.attach (button, 2, 0, 1, 2);
-
-      /* signal */
-      button.clicked.connect (() => {
-        // TODO: handly unlinking
-        });
-
-      this.linked_accounts_view.append (row_grid);
-    }
+    var personas = Utils.personas_as_list_model (individual);
+    var personas_filtered = new Gtk.FilterListModel (personas, new PersonaFilter ());
+    var personas_sorted = new Gtk.SortListModel (personas_filtered, new PersonaSorter ());
+    this.linked_accounts_view.bind_model (personas_sorted, create_row_for_persona);
+  }
+
+  private Gtk.Widget create_row_for_persona (GLib.Object item) {
+    unowned var persona = (Persona) item;
+
+    var row_grid = new Gtk.Grid ();
+
+    var image_frame = new Avatar (AVATAR_SIZE, individual);
+    image_frame.set_hexpand (false);
+    image_frame.margin_top = 6;
+    image_frame.margin_bottom = 6;
+    image_frame.margin_start = 6;
+    image_frame.margin_end = 12;
+    row_grid.attach (image_frame, 0, 0, 1, 2);
+
+    var display_name = new Gtk.Label ("");
+    display_name.set_halign (Gtk.Align.START);
+    display_name.set_valign (Gtk.Align.END);
+    display_name.set_hexpand (true);
+    display_name.set_markup (Markup.printf_escaped ("<span font='bold'>%s</span>", persona.display_id));
+
+    row_grid.attach (display_name, 1, 0, 1, 1);
+
+    var store_name = new Gtk.Label (Contacts.Utils.format_persona_store_name_for_contact (persona));
+    store_name.set_halign (Gtk.Align.START);
+    store_name.set_valign (Gtk.Align.START);
+    store_name.set_hexpand (true);
+    store_name.get_style_context ().add_class ("dim-label");
+    row_grid.attach (store_name, 1, 1, 1, 1);
+
+    var button = new Gtk.Button.with_label (_("Unlink"));
+    button.margin_end = 6;
+    button.set_valign (Gtk.Align.CENTER);
+    // button.get_child ().margin = 1; XXX
+    row_grid.attach (button, 2, 0, 1, 2);
+
+    /* signal */
+    button.clicked.connect (() => {
+      // TODO: handly unlinking
+      });
+
+    return row_grid;
   }
 }
diff --git a/src/contacts-persona-filter.vala b/src/contacts-persona-filter.vala
new file mode 100644
index 00000000..fa5cb4b5
--- /dev/null
+++ b/src/contacts-persona-filter.vala
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 Niels De Graef <nielsdegraef gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Folks;
+
+/**
+ * A custom GtkFilter to filter out {@link Folks.Persona}s, for example to
+ * exclude certain types of persona stores.
+ */
+public class Contacts.PersonaFilter : Gtk.Filter {
+
+  public string[] ignored_store_types {
+    get { return this._ignored_store_types; }
+    set {
+      if (value == null && this.ignored_store_types == null)
+        return;
+      if (GLib.strv_equal (this._ignored_store_types, value))
+        return;
+      // notify ignored-store-types
+    }
+  }
+  private string[] _ignored_store_types = { "key-file", };
+
+  public override bool match (GLib.Object? item) {
+    unowned var persona = item as Persona;
+    return_val_if_fail (persona != null, false);
+
+    return match_persona_store_type (persona);
+  }
+
+  private bool match_persona_store_type (Persona persona) {
+    return !(persona.store.type_id in this.ignored_store_types);
+  }
+
+  public override Gtk.FilterMatch get_strictness () {
+    return Gtk.FilterMatch.SOME;
+  }
+}
diff --git a/src/contacts-persona-sorter.vala b/src/contacts-persona-sorter.vala
new file mode 100644
index 00000000..4ba40060
--- /dev/null
+++ b/src/contacts-persona-sorter.vala
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 Niels De Graef <nielsdegraef gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Folks;
+
+/**
+ * A customer sorter that provides a consistent way of sorting
+ * {@link Folks.Persona}s within the whole application.
+ */
+public class Contacts.PersonaSorter : Gtk.Sorter {
+
+  public override Gtk.SorterOrder get_order () {
+    return Gtk.SorterOrder.PARTIAL;
+  }
+
+  public override Gtk.Ordering compare (Object? item1, Object? item2) {
+    unowned var persona_1 = (Persona) item1;
+    unowned var persona_2 = (Persona) item2;
+    unowned var store_1 = persona_1.store;
+    unowned var store_2 = persona_2.store;
+
+    // In the same store, sort Google 'other' contacts last
+    if (store_1 == store_2) {
+      if (!Utils.persona_is_google (persona_1))
+        return Gtk.Ordering.EQUAL;
+
+      var p1_is_other = Utils.persona_is_google_other (persona_1);
+      if (p1_is_other != Utils.persona_is_google_other (persona_2))
+        return p1_is_other? Gtk.Ordering.LARGER : Gtk.Ordering.SMALLER;
+    }
+
+    // Sort primary stores before others
+    if (store_1.is_primary_store != store_2.is_primary_store)
+      return (store_1.is_primary_store)? Gtk.Ordering.SMALLER : Gtk.Ordering.LARGER;
+
+    // E-D-S stores get prioritized
+    if ((store_1.type_id == "eds") != (store_2.type_id == "eds"))
+      return (store_1.type_id == "eds")? Gtk.Ordering.SMALLER : Gtk.Ordering.LARGER;
+
+    // Normal case: use alphabetical sorting
+    return Gtk.Ordering.from_cmpfunc (strcmp (store_1.id, store_2.id));
+  }
+}
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index 012b8db3..ad828335 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -264,39 +264,11 @@ namespace Contacts.Utils {
     return false;
   }
 
-  public ListModel get_personas_for_display (Individual individual) {
-    var persona_list = new ListStore(typeof(Persona));
+  public ListModel personas_as_list_model (Individual individual) {
+    var personas = new ListStore (typeof(Persona));
     foreach (var persona in individual.personas)
-      if (persona.store.type_id != "key-file")
-        persona_list.append (persona);
-
-    persona_list.sort ((a, b) => {
-      unowned var store_a = ((Persona) a).store;
-      unowned var store_b = ((Persona) b).store;
-
-      // In the same store, sort Google 'other' contacts last
-      if (store_a == store_b) {
-        if (!persona_is_google ((Persona) a))
-          return 0;
-
-        var a_is_other = persona_is_google_other ((Persona) a);
-        if (a_is_other != persona_is_google_other ((Persona) b))
-          return a_is_other? 1 : -1;
-      }
-
-      // Sort primary stores before others
-      if (store_a.is_primary_store != store_b.is_primary_store)
-        return (store_a.is_primary_store)? -1 : 1;
-
-      // E-D-S stores get prioritized
-      if ((store_a.type_id == "eds") != (store_b.type_id == "eds"))
-        return (store_a.type_id == "eds")? -1 : 1;
-
-      // Normal case: use alphabetical sorting
-      return strcmp (store_a.id, store_b.id);
-    });
-
-    return persona_list;
+      personas.append (persona);
+    return personas;
   }
 
   public Persona? find_primary_persona (Individual individual) {
@@ -365,7 +337,7 @@ namespace Contacts.Utils {
     return all_unlinkable;
   }
 
-  private bool persona_is_google (Persona persona) {
+  public bool persona_is_google (Persona persona) {
     return persona.store.type_id == "eds" && esource_uid_is_google (persona.store.id);
   }
 
diff --git a/src/meson.build b/src/meson.build
index 0710d4a9..96d455f3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,6 +17,8 @@ libcontacts_sources = files(
   'contacts-link-operation.vala',
   'contacts-operation.vala',
   'contacts-operation-list.vala',
+  'contacts-persona-filter.vala',
+  'contacts-persona-sorter.vala',
   'contacts-query-filter.vala',
   'contacts-store.vala',
   'contacts-typeset.vala',


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