diff --git a/po/POTFILES.in b/po/POTFILES.in index 2650ca2b2292ba3c4e4dc493b988bd9ffc6e3d10..f40b93e453c77bb4b765140c539ee9d02cb5e66d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -32,6 +32,7 @@ src/contacts-link-suggestion-grid.vala src/contacts-settings.vala src/contacts-setup-window.vala src/contacts-typeset.vala +src/contacts-type-descriptor.vala src/contacts-window.vala src/main.vala src/org.gnome.Contacts.gschema.xml diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 56a88a33bba13788df3636de4f658e9c1832ffb3..5adf6c0eaf9765ff1626727aa33842903820d83e 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -20,5 +20,6 @@ src/contacts-link-suggestion-grid.c src/contacts-settings.c src/contacts-setup-window.c src/contacts-typeset.c +src/contacts-type-descriptor.c src/contacts-window.c src/main.c diff --git a/src/contacts-type-combo.vala b/src/contacts-type-combo.vala index d9186be89acc225b81fd434a9a5bf780c0d7ea65..aea3c6e6487556f1d94186701aa5a8cefe490849 100644 --- a/src/contacts-type-combo.vala +++ b/src/contacts-type-combo.vala @@ -77,7 +77,7 @@ public class Contacts.TypeCombo : Grid { if (text != "") { TreeIter iter; - type_set.add_custom_label (text, out iter); + type_set.get_iter_for_custom_label (text, out iter); last_active = iter; combo.set_active_iter (iter); @@ -113,15 +113,8 @@ public class Contacts.TypeCombo : Grid { modified = true; TreeIter iter; if (combo.get_active_iter (out iter)) { - if (type_set.is_custom (iter)) { - custom_mode = true; - entry.show (); - entry.grab_focus (); - combo.hide (); - } else { - last_active = iter; - this.changed (); - } + last_active = iter; + this.changed (); } } @@ -135,19 +128,24 @@ public class Contacts.TypeCombo : Grid { public void set_active (AbstractFieldDetails details) { TreeIter iter; - type_set.lookup_type (details, out iter); + type_set.get_iter_for_field_details (details, out iter); set_from_iter (iter); } public void set_to (string type) { TreeIter iter; - type_set.lookup_type_by_string (type, out iter); + type_set.get_iter_for_vcard_type (type, out iter); set_from_iter (iter); } public void update_details (AbstractFieldDetails details) { TreeIter iter; combo.get_active_iter (out iter); - type_set.update_details (details, iter); + + TypeDescriptor descriptor; + string display_name; + combo.model.get (iter, 0, out display_name, 1, out descriptor); + assert (display_name != null); // Not separator + descriptor.save_to_field_details (details); } } diff --git a/src/contacts-type-descriptor.vala b/src/contacts-type-descriptor.vala new file mode 100644 index 0000000000000000000000000000000000000000..efc96cece0a40d55051f82c7b23c74281df8311d --- /dev/null +++ b/src/contacts-type-descriptor.vala @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2018 Niels De Graef + * + * 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 . + */ + +using Gtk; +using Gee; +using Folks; + +/** + * The TypeDescriptor is the internal representation of a property's type. + */ +public class Contacts.TypeDescriptor : Object { + + public const string X_GOOGLE_LABEL = "x-google-label"; + + private enum Source { + VCARD, + OTHER, + CUSTOM; + + public string to_string () { + switch (this) { + case VCARD: + return "vcard"; + case OTHER: + return "other"; + case CUSTOM: + return "custom"; + } + return "INVALID"; + } + } + + private Source source; + public string? name = null; + public string[]? vcard_types = null; + public TreeIter iter; + + /** + * Returns the translated name for this property. + */ + public string display_name { + get { + if (is_custom ()) + return this.name; + return dgettext (Config.GETTEXT_PACKAGE, this.name); + } + } + + /** + * Creates a TypeDescriptor which is mappable to the given VCard Type strings. + */ + public TypeDescriptor.vcard (string untranslated_name, string[] types) { + this.source = Source.VCARD; + this.name = untranslated_name; + this.vcard_types = types; + } + + /** + * Creates a TypeDescriptor with a custom label + */ + public TypeDescriptor.custom (string name) { + this.source = Source.CUSTOM; + this.name = name; + } + + /** + * Creates a TypeDescriptor which represents all non-representable types. + */ + public TypeDescriptor.other () { + this.source = Source.OTHER; + this.name = N_("Other"); + } + + public bool is_custom () { + return this.source == Source.CUSTOM; + } + + public void save_to_field_details (AbstractFieldDetails details) { + debug ("Saving type %s", to_string ()); + + var old_parameters = details.parameters; + var new_parameters = new HashMultiMap (); + + // Check whether PREF VCard "flag" is set + bool has_pref = false; + foreach (var val in old_parameters["type"]) { + if (val.ascii_casecmp ("PREF") == 0) { + has_pref = true; + break; + } + } + + // Copy over all parameters, execept the ones we're going to create ourselves + foreach (var param in old_parameters.get_keys ()) { + if (param != "type" && param != X_GOOGLE_LABEL) + foreach (var val in old_parameters[param]) + new_parameters[param] = val; + } + + // Set the type based on our Source + switch (this.source) { + case Source.VCARD: + foreach (var type in this.vcard_types) + if (type != null) + new_parameters["type"] = type; + break; + case Source.OTHER: + new_parameters["type"] = "OTHER"; + break; + case Source.CUSTOM: + new_parameters["type"] = "OTHER"; + new_parameters[X_GOOGLE_LABEL] = this.name; + break; + } + + if (has_pref) + new_parameters["type"] = "PREF"; + + // We didn't crash 'n burn, so lets + details.parameters = new_parameters; + } + + /** + * Converts the TypeDescriptor to a string. Should only be used for debugging. + */ + public string to_string () { + StringBuilder str = new StringBuilder ("{ "); + str.append_printf (".source = %s, ", this.source.to_string ()); + str.append_printf (".name = \"%s\", ", this.name); + str.append_printf (".display_name = \"%s\", ", this.display_name); + if (this.vcard_types == null) + str.append_printf (".vcard_types = NULL }"); + else + str.append_printf (".vcard_types = [ %s }", string.joinv (", ", this.vcard_types)); + return str.str; + } +} diff --git a/src/contacts-typeset.vala b/src/contacts-typeset.vala index 8b6329c9fb1919ff193e206515dd688e0f71a17b..4b5a5e4fb9c45bb964630dde9c19f8ccf15d4f57 100644 --- a/src/contacts-typeset.vala +++ b/src/contacts-typeset.vala @@ -19,261 +19,202 @@ using Gtk; using Gee; using Folks; +/** + * A TypeSet contains all the possible types of a property. For example, a + * phone number can be both for a personal phone, a work phone or even a fax + * machine. + */ public class Contacts.TypeSet : Object { - const string X_GOOGLE_LABEL = "x-google-label"; - const int MAX_TYPES = 3; - private struct InitData { - unowned string display_name_u; - unowned string types[3]; //MAX_TYPES - } - private class TypeDescriptor : Object { - public string display_name; // Translated - public GLib.List init_data; - public TreeIter iter; // Set if in_store - public bool in_store; - } + /** Returns the category of typeset (mostly used for debugging). */ + public string category { get; construct set; } - // Dummy TypeDescriptor to mark the "Custom..." store entry - private static TypeDescriptor custom_dummy = new TypeDescriptor (); // Dummy TypeDescriptor to mark the "Other..." store entry - private static TypeDescriptor other_dummy = new TypeDescriptor (); - - // Map from translated display name to TypeDescriptor for all "standard" types - private HashTable display_name_hash; - // Map from all type strings in to list of InitData with the data in it - private HashTable > vcard_lookup_hash; - // Map from display name to TreeIter for all custom types - private HashTable custom_hash; - - public Gtk.ListStore store; - private TreeIter other_iter; - - private TypeSet () { - display_name_hash = new HashTable (str_hash, str_equal); - vcard_lookup_hash = new HashTable > (str_hash, str_equal); - custom_hash = new HashTable (str_hash, str_equal); - - store = new Gtk.ListStore (2, - // Display name or null for separator - typeof(string?), - // TypeDescriptor for standard types, null for custom - typeof (TypeDescriptor)); - } + private TypeDescriptor other_dummy = new TypeDescriptor.other (); - private void add_descriptor_to_store (TypeDescriptor descriptor, bool is_custom) { - if (descriptor.in_store) - return; + // List of VcardTypeMapping. This makes sure of keeping the correct order + private Gee.List vcard_type_mappings + = new Gee.ArrayList (); - descriptor.in_store = true; - if (is_custom) - this.store.insert_before (out descriptor.iter, null); - else - this.store.append (out descriptor.iter); + // Contains 2 columns: + // 1. The type's display name (or null for a separator) + // 2. The TypeDescriptor + public Gtk.ListStore store { get; private set; } - store.set (descriptor.iter, 0, descriptor.display_name, 1, descriptor); - } - - private void add_init_data (InitData *init_data) { - unowned string dn = dgettext (Config.GETTEXT_PACKAGE, init_data.display_name_u); - TypeDescriptor descriptor = display_name_hash.lookup (dn); - if (descriptor == null) { - descriptor = new TypeDescriptor (); - descriptor.display_name = dn; - display_name_hash.insert (dn, descriptor); - } + /** + * Creates a TypeSet for the given category, e.g. "phones" (used for debugging) + */ + private TypeSet (string? category) { + Object (category: category); - descriptor.init_data.append (init_data); - - for (int j = 0; j < MAX_TYPES && init_data.types[j] != null; j++) { - unowned string type = init_data.types[j]; - unowned GLib.List l = this.vcard_lookup_hash.lookup (type); - if (l != null) { - l.append (init_data); - } else { - GLib.List l2 = null; - l2.append (init_data); - this.vcard_lookup_hash.insert (type, (owned) l2); - } - } + this.store = new Gtk.ListStore (2, typeof (unowned string?), typeof (TypeDescriptor)); } - private void add_init_data_done (string[] standard_untranslated) { - foreach (var untranslated in standard_untranslated) { - var descriptor = display_name_hash.lookup (dgettext (Config.GETTEXT_PACKAGE, untranslated)); - if (descriptor != null) - add_descriptor_to_store (descriptor, false); - else - error ("Internal error: Can't find display name %s in TypeSet data", untranslated); - } - - store.append (out other_iter); - /* Refers to the type of the detail, could be Home, Work or Other for email, and the same - * for phone numbers, addresses, etc. */ - store.set (other_iter, 0, _("Other"), 1, other_dummy); + /** + * Returns the TreeIter which corresponds to the type of the given + * AbstractFieldDetails. + */ + public void get_iter_for_field_details (AbstractFieldDetails detail, out TreeIter iter) { + // Note that we shouldn't have null here, but it's there just to be sure. + var d = lookup_descriptor_for_field_details (detail); + iter = (d != null)? d.iter : other_dummy.iter; } - public void add_custom_label (string label, out TreeIter iter) { - // If we add a custom name equal to one of the standard ones, reuse that one - var descriptor = display_name_hash.lookup (label); - if (descriptor != null) { - add_descriptor_to_store (descriptor, true); - iter = descriptor.iter; - return; - } - - if (label == _("Other")) { - iter = other_iter; - return; - } + /** + * Returns the TreeIter which corresponds the best to the given vcard type. + * @param type: A VCard-like type, such as "HOME" or "CELL". + */ + public void get_iter_for_vcard_type (string type, out TreeIter iter) { + unowned TypeDescriptor? d = lookup_descriptor_by_vcard_type (type); + iter = (d != null)? d.iter : this.other_dummy.iter; + } - unowned TreeIter? iterp = custom_hash.lookup (label); - if (iterp != null) { - iter = iterp; - return; - } + /** + * Returns the TreeIter which corresponds the best to the given custom label. + */ + public void get_iter_for_custom_label (string label, out TreeIter iter) { + var descr = get_descriptor_for_custom_label (label); + if (descr == null) + descr = create_descriptor_for_custom_label (label); + iter = descr.iter; + } - store.insert_before (out iter, null); - store.set (iter, 0, label, 1, null); - custom_hash.insert (label, iter); + /** + * Returns the display name for the type of the given AbstractFieldDetails. + */ + public string format_type (AbstractFieldDetails detail) { + var d = lookup_descriptor_for_field_details (detail); + return (d != null)? d.display_name : _("Other"); } - private unowned TypeDescriptor? lookup_descriptor_by_string (string str) { - unowned GLib.List? l = vcard_lookup_hash.lookup (str); - foreach (unowned InitData *d in l) { - if (d.types[1] == null) { - unowned string dn = dgettext (Config.GETTEXT_PACKAGE, d.display_name_u); - return display_name_hash.lookup (dn); - } - } + /** + * Adds the TypeDescriptor to the {@link Typeset}'s store. + * @param descriptor: The TypeDescription to be added + */ + private void add_descriptor_to_store (TypeDescriptor descriptor) { + debug ("%s: Adding type %s to store", this.category, descriptor.to_string ()); - return null; - } + if (descriptor.is_custom ()) + this.store.insert_before (out descriptor.iter, null); + else + this.store.append (out descriptor.iter); + store.set (descriptor.iter, 0, descriptor.display_name, 1, descriptor); + } - private unowned TypeDescriptor? lookup_descriptor (AbstractFieldDetails detail) { - var i = detail.get_parameter_values ("type"); - if (i == null || i.is_empty) + /** + * Returns the TypeDescriptor for the given display name in the + * {@link Typeset}'s store, if any. + * + * @param display_name: The translated display name + * @return: The appropriate TypeDescriptor or null if no match was found. + */ + public unowned TypeDescriptor? lookup_descriptor_in_store (string display_name) { + TreeIter iter; + + // Make sure we handle an empty store + if (!this.store.get_iter_first (out iter)) return null; - var list = new Gee.ArrayList (); - foreach (var s in detail.get_parameter_values ("type")) - list.add (s.up ()); - - // Make sure all items in the InitData is in the specified type, there might - // be more, but we ignore them (so a HOME,FOO,PREF,BLAH contact still matches - // the standard HOME one, but not HOME,FAX - unowned GLib.List? l = vcard_lookup_hash.lookup (list[0]); - foreach (unowned InitData *d in l) { - bool all_found = true; - for (int j = 0; j < MAX_TYPES && d.types[j] != null; j++) { - if (!list.contains (d.types[j])) { - all_found = false; - break; - } - } - if (all_found) { - unowned string dn = dgettext (Config.GETTEXT_PACKAGE, d.display_name_u); - return display_name_hash.lookup (dn); - } - } + do { + unowned TypeDescriptor? type_descr; + this.store.get (iter, 1, out type_descr); + if (display_name.ascii_casecmp (type_descr.display_name) == 0) + return type_descr; + if (display_name.ascii_casecmp (type_descr.name) == 0) + return type_descr; + } while (this.store.iter_next (ref iter)); + + // Nothing was found return null; } - // Looks up (and creates if necessary) the type in the store - public void lookup_type (AbstractFieldDetails detail, out TreeIter iter) { - if (detail.parameters.contains (X_GOOGLE_LABEL)) { - var label = Utils.get_first (detail.parameters.get (X_GOOGLE_LABEL)); - add_custom_label (label, out iter); - return; + private void add_vcard_mapping (VcardTypeMapping vcard_mapping) { + TypeDescriptor? descriptor = lookup_descriptor_in_store (vcard_mapping.name); + if (descriptor == null) { + descriptor = new TypeDescriptor.vcard (vcard_mapping.name, vcard_mapping.types); + add_descriptor_to_store (descriptor); } - unowned TypeDescriptor? d = lookup_descriptor (detail); - if (d != null) { - add_descriptor_to_store (d, true); - iter = d.iter; - } else { - iter = other_iter; - } + this.vcard_type_mappings.add (vcard_mapping); } - public void lookup_type_by_string (string type, out TreeIter iter) { - unowned TypeDescriptor? d = lookup_descriptor_by_string (type); - iter = (d != null)? d.iter : this.other_iter; + // Refers to the type of the detail, i.e. "Other" instead of "Personal" or "Work" + private void add_type_other () { + store.append (out other_dummy.iter); + store.set (other_dummy.iter, 0, other_dummy.display_name, 1, other_dummy); } - public string format_type (AbstractFieldDetails detail) { - if (detail.parameters.contains (X_GOOGLE_LABEL)) - return Utils.get_first (detail.parameters[X_GOOGLE_LABEL]); + /** + * Tries to find the TypeDescriptor matching the given custom label, or null if none. + */ + public unowned TypeDescriptor? get_descriptor_for_custom_label (string label) { + // Check in the current display names + unowned TypeDescriptor? descriptor = lookup_descriptor_in_store (label); + if (descriptor != null) + return descriptor; + + // Try again, but use the vcard types too + descriptor = lookup_descriptor_by_vcard_type (label); + return descriptor; + } - unowned TypeDescriptor? d = lookup_descriptor (detail); - return (d != null)? d.display_name : _("Other"); + private TypeDescriptor create_descriptor_for_custom_label (string label) { + var new_descriptor = new TypeDescriptor.custom (label); + add_descriptor_to_store (new_descriptor); + return new_descriptor; } - public void update_details (AbstractFieldDetails details, TreeIter iter) { - var old_parameters = details.parameters; - details.parameters = new HashMultiMap (); - bool has_pref = false; - foreach (var val in old_parameters["type"]) { - if (val.ascii_casecmp ("PREF") == 0) { - has_pref = true; - break; - } + /** + * Returns the TypeDescriptor which corresponds the best to the given vcard type. + * @param str: A VCard-like type, such as "HOME" or "CELL". + */ + private unowned TypeDescriptor? lookup_descriptor_by_vcard_type (string str) { + foreach (VcardTypeMapping? mapping in this.vcard_type_mappings) { + if (mapping.contains (str)) + return lookup_descriptor_in_store (mapping.name); } - foreach (var param in old_parameters.get_keys()) { - if (param != "type" && param != X_GOOGLE_LABEL) - foreach (var val in old_parameters[param]) - details.parameters[param] = val; + + return null; + } + + private TypeDescriptor? lookup_descriptor_for_field_details (AbstractFieldDetails detail) { + if (detail.parameters.contains (TypeDescriptor.X_GOOGLE_LABEL)) { + var label = Utils.get_first (detail.parameters[TypeDescriptor.X_GOOGLE_LABEL]); + var descriptor = get_descriptor_for_custom_label (label); + // Still didn't find it => create it + if (descriptor == null) + descriptor = create_descriptor_for_custom_label (label); + return descriptor; } - TypeDescriptor descriptor; - string display_name; - store.get (iter, 0, out display_name, 1, out descriptor); - - assert (display_name != null); // Not separator - assert (descriptor != custom_dummy); // Not custom... - - if (descriptor == null) { // A custom label - details.parameters["type"] = "OTHER"; - details.parameters[X_GOOGLE_LABEL] = display_name; - } else { - if (descriptor == other_dummy) { - details.parameters["type"] = "OTHER"; - } else { - InitData *init_data = descriptor.init_data.data; - for (int j = 0; j < MAX_TYPES && init_data.types[j] != null; j++) - details.parameters["type"] = init_data.types[j]; - } + var types = detail.get_parameter_values ("type"); + if (types == null || types.is_empty) + return null; + + foreach (VcardTypeMapping? d in this.vcard_type_mappings) { + if (d.matches (types)) + return lookup_descriptor_in_store (d.name); } - if (has_pref) - details.parameters["type"] = "PREF"; + return null; } - public bool is_custom (TreeIter iter) { - TypeDescriptor descriptor; - store.get (iter, 1, out descriptor); - return descriptor == custom_dummy; - } private static TypeSet _general; - private const InitData[] general_data = { + private const VcardTypeMapping[] general_data = { // List most specific first, always in upper case { N_("Home"), { "HOME" } }, { N_("Work"), { "WORK" } } }; public static TypeSet general { get { - string[] standard = { - "Work", "Home" - }; - if (_general == null) { - _general = new TypeSet (); + _general = new TypeSet ("General"); for (int i = 0; i < general_data.length; i++) - _general.add_init_data (&general_data[i]); - _general.add_init_data_done (standard); + _general.add_vcard_mapping (general_data[i]); + _general.add_type_other (); } return _general; @@ -281,7 +222,7 @@ public class Contacts.TypeSet : Object { } private static TypeSet _email; - private const InitData[] email_data = { + private const VcardTypeMapping[] email_data = { // List most specific first, always in upper case { N_("Personal"), { "PERSONAL" } }, { N_("Home"), { "HOME" } }, @@ -289,15 +230,11 @@ public class Contacts.TypeSet : Object { }; public static TypeSet email { get { - string[] standard = { - "Personal", "Home", "Work" - }; - if (_email == null) { - _email = new TypeSet (); + _email = new TypeSet ("Emails"); for (int i = 0; i < email_data.length; i++) - _email.add_init_data (&email_data[i]); - _email.add_init_data_done (standard); + _email.add_vcard_mapping (email_data[i]); + _email.add_type_other (); } return _email; @@ -305,41 +242,36 @@ public class Contacts.TypeSet : Object { } private static TypeSet _phone; + private const VcardTypeMapping[] phone_data = { + // List most specific first, always in upper case + { N_("Assistant"), { "X-EVOLUTION-ASSISTANT" } }, + { N_("Work"), { "WORK", "VOICE" } }, + { N_("Work Fax"), { "WORK", "FAX" } }, + { N_("Work"), { "WORK" } }, + { N_("Callback"), { "X-EVOLUTION-CALLBACK" } }, + { N_("Car"), { "CAR" } }, + { N_("Company"), { "X-EVOLUTION-COMPANY" } }, + { N_("Home"), { "HOME", "VOICE" } }, + { N_("Home Fax"), { "HOME", "FAX" } }, + { N_("Home"), { "HOME" } }, + { N_("ISDN"), { "ISDN" } }, + { N_("Mobile"), { "CELL" } }, + { N_("Other"), { "VOICE" } }, + { N_("Fax"), { "FAX" } }, + { N_("Pager"), { "PAGER" } }, + { N_("Radio"), { "X-EVOLUTION-RADIO" } }, + { N_("Telex"), { "X-EVOLUTION-TELEX" } }, + /* To translators: TTY is Teletypewriter */ + { N_("TTY"), { "X-EVOLUTION-TTYTDD" } } + }; public static TypeSet phone { get { - const InitData[] data = { - // List most specific first, always in upper case - { N_("Assistant"), { "X-EVOLUTION-ASSISTANT" } }, - { N_("Work"), { "WORK", "VOICE" } }, - { N_("Work Fax"), { "WORK", "FAX" } }, - { N_("Callback"), { "X-EVOLUTION-CALLBACK" } }, - { N_("Car"), { "CAR" } }, - { N_("Company"), { "X-EVOLUTION-COMPANY" } }, - { N_("Home"), { "HOME", "VOICE" } }, - { N_("Home Fax"), { "HOME", "FAX" } }, - { N_("ISDN"), { "ISDN" } }, - { N_("Mobile"), { "CELL" } }, - { N_("Other"), { "VOICE" } }, - { N_("Fax"), { "FAX" } }, - { N_("Pager"), { "PAGER" } }, - { N_("Radio"), { "X-EVOLUTION-RADIO" } }, - { N_("Telex"), { "X-EVOLUTION-TELEX" } }, - /* To translators: TTY is Teletypewriter */ - { N_("TTY"), { "X-EVOLUTION-TTYTDD" } } - }; - - // Make sure these strings are the same as the above - string[] standard = { - "Mobile", "Work", "Home" - }; if (_phone == null) { - _phone = new TypeSet (); - for (int i = 0; i < data.length; i++) - _phone.add_init_data (&data[i]); - for (int i = 0; i < general_data.length; i++) - _phone.add_init_data (&general_data[i]); - _phone.add_init_data_done (standard); + _phone = new TypeSet ("Phones"); + for (int i = 0; i < phone_data.length; i++) + _phone.add_vcard_mapping (phone_data[i]); + _phone.add_type_other (); } return _phone; diff --git a/src/contacts-vcard-type-mapping.vala b/src/contacts-vcard-type-mapping.vala new file mode 100644 index 0000000000000000000000000000000000000000..35ce3e030a52a8b3d62fb121665c1c89bf5ba3e9 --- /dev/null +++ b/src/contacts-vcard-type-mapping.vala @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Niels De Graef + * + * 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 . + */ + +using Gtk; +using Gee; +using Folks; + +/** + * The VcardTypeMapping struct is used to map known vcard types to a display + * name. It also contains the logic when a vard-like type string matches with + * another. + */ +internal struct Contacts.VcardTypeMapping { + unowned string name; // untranslated + unowned string types[3]; //MAX_TYPES + private const int MAX_TYPES = 3; + + /** Returns whether the mapping contains the given vcard type. */ + public bool contains (string type) { + for (int i = 0; i < MAX_TYPES && this.types[i] != null; i++) + if (types_are_equal (this.types[i], type)) + return true; + return false; + } + + /** + * Checks whether all items in the VcardTypeMapping are in the specified @types. + * Even though there might be other values in @types, we ignore them. + * + * For example: [ HOME, FOO, PREF, BLAH ] should match the [ HOME ] VCard + * type, but not [ HOME, FAX ] + */ + public bool matches (Collection types) { + for (int i = 0; i < MAX_TYPES && this.types[i] != null; i++) { + bool occurs_in_list = true; + foreach (var type in types) { + if (!types_are_equal (type, this.types[i])) { + occurs_in_list = false; + break; + } + } + + if (!occurs_in_list) + return false; + } + return true; + } + + private static bool types_are_equal (string a, string b) { + return a.ascii_casecmp (b) == 0; + } +} diff --git a/src/meson.build b/src/meson.build index f3e69c61303973dac6b6de55d85faff80b11db1a..773c3dcf8739f0aaa3a1fda8960b606e49588871 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,7 +12,9 @@ libcontacts_sources = files( 'contacts-im-service.vala', 'contacts-store.vala', 'contacts-typeset.vala', + 'contacts-type-descriptor.vala', 'contacts-utils.vala', + 'contacts-vcard-type-mapping.vala', ) contacts_vala_args = [