[gnome-contacts/nielsdg/filters-and-sorters: 4/4] Create a custom sorter for AbstractFieldDetails




commit 3f2f996accb803ed14828f8c2a6834a2324569c5
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Wed Aug 17 01:22:38 2022 +0200

    Create a custom sorter for AbstractFieldDetails
    
    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-abstract-field-details-sorter.vala | 75 +++++++++++++++++++++++++
 src/contacts-contact-sheet.vala                 | 23 +++++---
 src/contacts-editor-property.vala               | 19 ++++---
 src/contacts-utils.vala                         | 56 ++----------------
 src/meson.build                                 |  1 +
 5 files changed, 109 insertions(+), 65 deletions(-)
---
diff --git a/src/contacts-abstract-field-details-sorter.vala b/src/contacts-abstract-field-details-sorter.vala
new file mode 100644
index 00000000..430cac5d
--- /dev/null
+++ b/src/contacts-abstract-field-details-sorter.vala
@@ -0,0 +1,75 @@
+/*
+ * 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 sorter that provides a consistent way of sorting
+ * {@link Folks.AbstractFieldDetails} within the whole application.
+ */
+public class Contacts.AbstractFieldDetailsSorter : Gtk.Sorter {
+
+  public override Gtk.SorterOrder get_order () {
+    return Gtk.SorterOrder.PARTIAL;
+  }
+
+  public override Gtk.Ordering compare (Object? item1, Object? item2) {
+    unowned var a = (AbstractFieldDetails) item1;
+    unowned var b = (AbstractFieldDetails) item2;
+
+    // Fields with a PREF hint always go first (see VCard PREF attribute)
+    var a_has_pref = has_pref (a);
+    if (a_has_pref != has_pref (b))
+      return (a_has_pref)? Gtk.Ordering.SMALLER : Gtk.Ordering.LARGER;
+
+    // sort by field type first (e.g. "Home", "Work")
+    unowned var type_set = select_typeset_from_fielddetails (a);
+    var result = type_set.format_type (a).ascii_casecmp (type_set.format_type (b));
+    if (result != 0)
+      return Gtk.Ordering.from_cmpfunc (result);
+
+    // Try to compare by value if types are equal
+    unowned var aa = a as AbstractFieldDetails<string>;
+    unowned var bb = b as AbstractFieldDetails<string>;
+    if (aa != null && bb != null)
+      return Gtk.Ordering.from_cmpfunc (strcmp (aa.value, bb.value));
+
+    // No heuristics to fall back to.
+    warning ("Unsupported AbstractFieldDetails value type");
+    return Gtk.Ordering.EQUAL;
+  }
+
+  private bool has_pref (AbstractFieldDetails details) {
+    var evolution_pref = details.get_parameter_values ("x-evolution-ui-slot");
+    if (evolution_pref != null && Utils.get_first (evolution_pref) == "1")
+      return true;
+
+    foreach (var param in details.parameters["type"]) {
+      if (param.ascii_casecmp ("PREF") == 0)
+        return true;
+    }
+    return false;
+  }
+
+  private unowned TypeSet select_typeset_from_fielddetails (AbstractFieldDetails a) {
+    if (a is EmailFieldDetails)
+      return TypeSet.email;
+    if (a is PhoneFieldDetails)
+      return TypeSet.phone;
+    return TypeSet.general;
+  }
+}
diff --git a/src/contacts-contact-sheet.vala b/src/contacts-contact-sheet.vala
index 1b0eecff..81b32937 100644
--- a/src/contacts-contact-sheet.vala
+++ b/src/contacts-contact-sheet.vala
@@ -223,9 +223,11 @@ public class Contacts.ContactSheet : Gtk.Grid {
     if (details == null)
       return;
 
-    var roles = Utils.sort_fields<RoleFieldDetails>(details.roles);
+    var roles = Utils.fields_to_sorted (details.roles);
     var rows = new GLib.List<Gtk.ListBoxRow> ();
-    foreach (var role in roles) {
+    for (uint i = 0; i < roles.get_n_items (); i++) {
+      var role = (RoleFieldDetails) roles.get_item (i);
+
       if (role.value.is_empty ())
         continue;
 
@@ -254,9 +256,11 @@ public class Contacts.ContactSheet : Gtk.Grid {
     if (details == null)
       return;
 
-    var emails = Utils.sort_fields<EmailFieldDetails>(details.email_addresses);
+    var emails = Utils.fields_to_sorted (details.email_addresses);
     var rows = new GLib.List<Gtk.ListBoxRow> ();
-    foreach (var email in emails) {
+    for (uint i = 0; i < emails.get_n_items (); i++) {
+      var email = (EmailFieldDetails) emails.get_item (i);
+
       if (email.value == "")
         continue;
 
@@ -281,9 +285,11 @@ public class Contacts.ContactSheet : Gtk.Grid {
     if (phone_details == null)
       return;
 
-    var phones = Utils.sort_fields<PhoneFieldDetails>(phone_details.phone_numbers);
+    var phones = Utils.fields_to_sorted (phone_details.phone_numbers);
     var rows = new GLib.List<Gtk.ListBoxRow> ();
-    foreach (var phone in phones) {
+    for (uint i = 0; i < phones.get_n_items (); i++) {
+      var phone = (PhoneFieldDetails) phones.get_item (i);
+
       if (phone.value == "")
         continue;
 
@@ -336,7 +342,10 @@ public class Contacts.ContactSheet : Gtk.Grid {
       return;
 
     var rows = new GLib.List<Gtk.ListBoxRow> ();
-    foreach (var url in url_details.urls) {
+    var urls = Utils.fields_to_sorted (url_details.urls);
+    for (uint i = 0; i < urls.get_n_items (); i++) {
+      var url = (UrlFieldDetails) urls.get_item (i);
+
       if (url.value == "")
         continue;
 
diff --git a/src/contacts-editor-property.vala b/src/contacts-editor-property.vala
index fe6aed6a..85c40e27 100644
--- a/src/contacts-editor-property.vala
+++ b/src/contacts-editor-property.vala
@@ -440,9 +440,10 @@ public class Contacts.EditorProperty : Object, ListModel {
       case "email-addresses":
         unowned var details = p as EmailDetails;
         if (details != null) {
-          var emails = Utils.sort_fields<EmailFieldDetails>(details.email_addresses);
+          var emails = Utils.fields_to_sorted (details.email_addresses);
           if (!only_new)
-            foreach (var email in emails) {
+            for (uint i = 0; i < emails.get_n_items (); i++) {
+              var email = (EmailFieldDetails) emails.get_item (i);
               this.rows.add (create_for_email (details.email_addresses, email));
             }
           if (this.writeable)
@@ -452,9 +453,10 @@ public class Contacts.EditorProperty : Object, ListModel {
       case "phone-numbers":
         unowned var details = p as PhoneDetails;
         if (details != null) {
-          var phones = Utils.sort_fields<PhoneFieldDetails>(details.phone_numbers);
+          var phones = Utils.fields_to_sorted (details.phone_numbers);
           if (!only_new)
-            foreach (var phone in phones) {
+            for (uint i = 0; i < phones.get_n_items (); i++) {
+              var phone = (PhoneFieldDetails) phones.get_item (i);
               this.rows.add (create_for_phone (details.phone_numbers, phone));
             }
           if (this.writeable)
@@ -464,9 +466,10 @@ public class Contacts.EditorProperty : Object, ListModel {
       case "urls":
         unowned var details = p as UrlDetails;
         if (details != null) {
-          var urls = Utils.sort_fields<UrlFieldDetails>(details.urls);
+          var urls = Utils.fields_to_sorted (details.urls);
           if (!only_new)
-            foreach (var url in urls) {
+            for (uint i = 0; i < urls.get_n_items (); i++) {
+              var url = (UrlFieldDetails) urls.get_item (i);
               this.rows.add (create_for_url (details.urls, url));
             }
           this.rows.add (create_for_url (details.urls));
@@ -510,7 +513,9 @@ public class Contacts.EditorProperty : Object, ListModel {
         unowned var role_details = p as RoleDetails;
         if (role_details != null) {
           if (!only_new) {
-            foreach (var role in role_details.roles) {
+            var roles = Utils.fields_to_sorted (role_details.roles);
+            for (uint i = 0; i < roles.get_n_items (); i++) {
+              var role = (RoleFieldDetails) roles.get_item (i);
               this.rows.add (create_for_role (role_details.roles, role));
             }
           }
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index f0d2d436..4bfd8106 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -134,57 +134,11 @@ namespace Contacts.Utils {
     return !has_main_persona (self) || !has_mainable_persona (other);
   }
 
-  private bool has_pref (AbstractFieldDetails details) {
-    var evolution_pref = details.get_parameter_values ("x-evolution-ui-slot");
-    if (evolution_pref != null && Utils.get_first (evolution_pref) == "1")
-      return true;
-
-    foreach (var param in details.parameters["type"]) {
-      if (param.ascii_casecmp ("PREF") == 0)
-        return true;
-    }
-    return false;
-  }
-
-  private TypeSet select_typeset_from_fielddetails (AbstractFieldDetails a) {
-    if (a is EmailFieldDetails)
-      return TypeSet.email;
-    if (a is PhoneFieldDetails)
-      return TypeSet.phone;
-    return TypeSet.general;
-  }
-
-  public int compare_fields (void* _a, void* _b) {
-    var a = (AbstractFieldDetails) _a;
-    var b = (AbstractFieldDetails) _b;
-
-    // Fields with a PREF hint always go first (see VCard PREF attribute)
-    var a_has_pref = has_pref (a);
-    if (a_has_pref != has_pref (b))
-      return (a_has_pref)? -1 : 1;
-
-    // sort by field type first (e.g. "Home", "Work")
-    var type_set = select_typeset_from_fielddetails (a);
-    var result = type_set.format_type (a).ascii_casecmp (type_set.format_type (b));
-    if (result != 0)
-      return result;
-
-    // Try to compare by value if types are equal
-    var aa = a as AbstractFieldDetails<string>;
-    var bb = b as AbstractFieldDetails<string>;
-    if (aa != null && bb != null)
-      return strcmp (aa.value, bb.value);
-
-    // No heuristics to fall back to.
-    warning ("Unsupported AbstractFieldDetails value type");
-    return 0;
-  }
-
-  public Gee.List<T> sort_fields<T> (Gee.Collection<T> fields) {
-    var res = new Gee.ArrayList<T>();
-    res.add_all (fields);
-    res.sort (Contacts.Utils.compare_fields);
-    return res;
+  public ListModel fields_to_sorted (Gee.Collection<AbstractFieldDetails> fields) {
+    var res = new ListStore (typeof (AbstractFieldDetails));
+    foreach (var afd in fields)
+      res.append (afd);
+    return new Gtk.SortListModel ((owned) res, new AbstractFieldDetailsSorter ());
   }
 
   public string[] format_address (PostalAddress addr) {
diff --git a/src/meson.build b/src/meson.build
index 96d455f3..b439d601 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -8,6 +8,7 @@ install_data('org.gnome.Contacts.gschema.xml',
 
 # Common library
 libcontacts_sources = files(
+  'contacts-abstract-field-details-sorter.vala',
   'contacts-delete-operation.vala',
   'contacts-esd-setup.vala',
   'contacts-fake-persona-store.vala',


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