summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels De Graef <nielsdegraef@gmail.com>2018-07-30 00:42:28 +0200
committerNiels De Graef <nielsdegraef@gmail.com>2018-07-30 00:42:28 +0200
commitf8be56579981388b0453ca7e5af8e428f6a9ded8 (patch)
tree5524086e61a877b92ff3c98a4d487fa9de349ccc
parent190ebb74bdc6e3c849bf24356b665a7c6ea75ed1 (diff)
downloadgnome-contacts-f8be56579981388b0453ca7e5af8e428f6a9ded8.tar.gz
Split up TypeSet/TypeCombo in separate files.
In the meantime, clean up a bit: * Don't mix tabs/spaces * No longer use generic undescriptive names like Data (->TypeDescriptor) * The TypeCombo isn't needed by the search provider, so leave that out.
-rw-r--r--src/contacts-type-combo.vala153
-rw-r--r--src/contacts-types.vala496
-rw-r--r--src/contacts-typeset.vala348
-rw-r--r--src/meson.build3
4 files changed, 503 insertions, 497 deletions
diff --git a/src/contacts-type-combo.vala b/src/contacts-type-combo.vala
new file mode 100644
index 0000000..d9186be
--- /dev/null
+++ b/src/contacts-type-combo.vala
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl@redhat.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 Gtk;
+using Gee;
+using Folks;
+
+public class Contacts.TypeCombo : Grid {
+ private unowned TypeSet type_set;
+ private ComboBox combo;
+ private Entry entry;
+ private TreeIter last_active;
+ private bool custom_mode;
+ private bool in_manual_change;
+ public bool modified;
+
+ public signal void changed ();
+
+ public TypeCombo (TypeSet type_set) {
+ this.type_set = type_set;
+
+ combo = new ComboBox.with_model (type_set.store);
+ combo.set_halign (Align.FILL);
+ combo.set_hexpand (true);
+ this.add (combo);
+
+ var renderer = new CellRendererText ();
+ combo.pack_start (renderer, true);
+ combo.set_attributes (renderer,
+ "text", 0);
+ combo.set_row_separator_func ( (model, iter) => {
+ string? s;
+ model.get (iter, 0, out s);
+ return s == null;
+ });
+
+ entry = new Entry ();
+ entry.set_halign (Align.FILL);
+ entry.set_hexpand (true);
+ // Make the default entry small so we don't unnecessarily
+ // expand the labels (it'll be expanded a bit anyway)
+ entry.width_chars = 4;
+
+ this.add (entry);
+
+ combo.set_no_show_all (true);
+ entry.set_no_show_all (true);
+
+ combo.show ();
+
+ combo.changed.connect (combo_changed);
+ entry.focus_out_event.connect (entry_focus_out_event);
+ entry.activate.connect (entry_activate);
+ entry.key_release_event.connect (entry_key_release);
+ }
+
+ private void finish_custom () {
+ if (!custom_mode)
+ return;
+
+ custom_mode = false;
+ var text = entry.get_text ();
+
+ if (text != "") {
+ TreeIter iter;
+ type_set.add_custom_label (text, out iter);
+
+ last_active = iter;
+ combo.set_active_iter (iter);
+ } else {
+ combo.set_active_iter (last_active);
+ }
+
+ combo.show ();
+ entry.hide ();
+ }
+
+ private void entry_activate () {
+ finish_custom ();
+ }
+
+ private bool entry_key_release (Gdk.EventKey event) {
+ if (event.keyval == Gdk.Key.Escape) {
+ entry.set_text ("");
+ finish_custom ();
+ }
+ return true;
+ }
+
+ private bool entry_focus_out_event (Gdk.EventFocus event) {
+ finish_custom ();
+ return false;
+ }
+
+ private void combo_changed (ComboBox combo) {
+ if (in_manual_change)
+ return;
+
+ 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 ();
+ }
+ }
+ }
+
+ private void set_from_iter (TreeIter iter) {
+ in_manual_change = true;
+ last_active = iter;
+ combo.set_active_iter (iter);
+ in_manual_change = false;
+ modified = false;
+ }
+
+ public void set_active (AbstractFieldDetails details) {
+ TreeIter iter;
+ type_set.lookup_type (details, out iter);
+ set_from_iter (iter);
+ }
+
+ public void set_to (string type) {
+ TreeIter iter;
+ type_set.lookup_type_by_string (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);
+ }
+}
diff --git a/src/contacts-types.vala b/src/contacts-types.vala
deleted file mode 100644
index b90b91a..0000000
--- a/src/contacts-types.vala
+++ /dev/null
@@ -1,496 +0,0 @@
-/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
-/*
- * Copyright (C) 2011 Alexander Larsson <alexl@redhat.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 Gtk;
-using Gee;
-using Folks;
-
-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 Data : Object {
- public string display_name; // Translated
- public GLib.List<InitData*> init_data;
- public TreeIter iter; // Set if in_store
- public bool in_store;
- }
-
- // Dummy Data to mark the "Custom..." store entry
- private static Data custom_dummy = new Data ();
- // Dummy Data to mark the "Other..." store entry
- private static Data other_dummy = new Data ();
-
- // Map from translated display name to Data for all "standard" types
- private HashTable<unowned string, Data> display_name_hash;
- // Map from all type strings in to list of InitData with the data in it
- private HashTable<unowned string, GLib.List<InitData*> > vcard_lookup_hash;
- // Map from display name to TreeIter for all custom types
- private HashTable<string, TreeIter?> custom_hash;
-
- public Gtk.ListStore store;
- private TreeIter other_iter;
-
- private TypeSet () {
- display_name_hash = new HashTable<unowned string, Data> (str_hash, str_equal);
- vcard_lookup_hash = new HashTable<unowned string, GLib.List<InitData*> > (str_hash, str_equal);
- custom_hash = new HashTable<string, TreeIter? > (str_hash, str_equal);
-
- store = new Gtk.ListStore (2,
- // Display name or null for separator
- typeof(string?),
- // Data for standard types, null for custom
- typeof (Data));
- }
-
- private void add_data_to_store (Data data, bool is_custom) {
- if (data.in_store)
- return;
-
- data.in_store = true;
- if (is_custom)
- store.insert_before (out data.iter, null);
- else
- store.append (out data.iter);
-
- store.set (data.iter, 0, data.display_name, 1, data);
- }
-
- private void add_init_data (InitData *init_data) {
- unowned string dn = dgettext (Config.GETTEXT_PACKAGE, init_data.display_name_u);
- Data data = display_name_hash.lookup (dn);
- if (data == null) {
- data = new Data ();
- data.display_name = dn;
- display_name_hash.insert (dn, data);
- }
-
- data.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<InitData*> l = vcard_lookup_hash.lookup (type);
- if (l != null) {
- l.append (init_data);
- } else {
- GLib.List<InitData*> l2 = null;
- l2.append (init_data);
- vcard_lookup_hash.insert (type, (owned) l2);
- }
- }
- }
-
- private void add_init_data_done (string[] standard_untranslated) {
- foreach (var untranslated in standard_untranslated) {
- var data = display_name_hash.lookup (dgettext (Config.GETTEXT_PACKAGE, untranslated));
- if (data != null)
- add_data_to_store (data, 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);
- }
-
- 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 data = display_name_hash.lookup (label);
- if (data != null) {
- add_data_to_store (data, true);
- iter = data.iter;
- return;
- }
-
- if (label == _("Other")) {
- iter = other_iter;
- return;
- }
-
- unowned TreeIter? iterp = custom_hash.lookup (label);
- if (iterp != null) {
- iter = iterp;
- return;
- }
-
- store.insert_before (out iter, null);
- store.set (iter, 0, label, 1, null);
- custom_hash.insert (label, iter);
- }
-
- private unowned Data? lookup_data_by_string (string str) {
- unowned GLib.List<InitData *>? 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);
- }
- }
-
- return null;
- }
-
-
- private unowned Data? lookup_data (AbstractFieldDetails detail) {
- var i = detail.get_parameter_values ("type");
- if (i == null || i.is_empty)
- return null;
-
- var list = new Gee.ArrayList<string> ();
- 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<InitData *>? 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);
- }
- }
-
- 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<string> (detail.parameters.get (X_GOOGLE_LABEL));
- add_custom_label (label, out iter);
- return;
- }
-
- unowned Data? d = lookup_data (detail);
- if (d != null) {
- add_data_to_store (d, true);
- iter = d.iter;
- } else {
- iter = other_iter;
- }
- }
-
- public void lookup_type_by_string (string type, out TreeIter iter) {
- unowned Data? d = lookup_data_by_string (type);
- if (d != null) {
- iter = d.iter;
- } else {
- iter = other_iter;
- }
- }
-
- public string format_type (AbstractFieldDetails detail) {
- if (detail.parameters.contains (X_GOOGLE_LABEL)) {
- return Utils.get_first<string> (detail.parameters.get (X_GOOGLE_LABEL));
- }
-
- unowned Data? d = lookup_data (detail);
- if (d != null) {
- return d.display_name;
- }
-
- return _("Other");
- }
-
- public void update_details (AbstractFieldDetails details, TreeIter iter) {
- var old_parameters = details.parameters;
- details.parameters = new HashMultiMap<string, string> ();
- bool has_pref = false;
- foreach (var value in old_parameters.get ("type")) {
- if (value.ascii_casecmp ("PREF") == 0) {
- has_pref = true;
- break;
- }
- }
- foreach (var param in old_parameters.get_keys()) {
- if (param != "type" && param != X_GOOGLE_LABEL) {
- foreach (var value in old_parameters.get (param)) {
- details.parameters.set (param, value);
- }
- }
- }
-
- Data data;
- string display_name;
- store.get (iter, 0, out display_name, 1, out data);
-
- assert (display_name != null); // Not separator
- assert (data != custom_dummy); // Not custom...
-
- if (data == null) { // A custom label
- details.parameters.set ("type", "OTHER");
- details.parameters.set (X_GOOGLE_LABEL, display_name);
- } else {
- if (data == other_dummy) {
- details.parameters.set ("type", "OTHER");
- } else {
- InitData *init_data = data.init_data.data;
- for (int j = 0; j < MAX_TYPES && init_data.types[j] != null; j++) {
- details.parameters.set ("type", init_data.types[j]);
- }
- }
- }
-
- if (has_pref)
- details.parameters.set ("type", "PREF");
- }
-
- public bool is_custom (TreeIter iter) {
- Data data;
- store.get (iter, 1, out data);
- return data == custom_dummy;
- }
-
- private static TypeSet _general;
- private const InitData[] 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 ();
- for (int i = 0; i < general_data.length; i++)
- _general.add_init_data (&general_data[i]);
- _general.add_init_data_done (standard);
- }
-
- return _general;
- }
- }
-
- private static TypeSet _email;
- private const InitData[] email_data = {
- // List most specific first, always in upper case
- { N_("Personal"), { "PERSONAL" } },
- { N_("Home"), { "HOME" } },
- { N_("Work"), { "WORK" } }
- };
- public static TypeSet email {
- get {
- string[] standard = {
- "Personal", "Home", "Work"
- };
-
- if (_email == null) {
- _email = new TypeSet ();
- for (int i = 0; i < email_data.length; i++)
- _email.add_init_data (&email_data[i]);
- _email.add_init_data_done (standard);
- }
-
- return _email;
- }
- }
-
- private static TypeSet _phone;
- 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" } }
- // Also we add the general ones here
- };
-
- // 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);
- }
-
- return _phone;
- }
- }
-}
-
-public class Contacts.TypeCombo : Grid {
- private unowned TypeSet type_set;
- private ComboBox combo;
- private Entry entry;
- private TreeIter last_active;
- private bool custom_mode;
- private bool in_manual_change;
- public bool modified;
-
- public signal void changed ();
-
- public TypeCombo (TypeSet type_set) {
- this.type_set = type_set;
-
- combo = new ComboBox.with_model (type_set.store);
- combo.set_halign (Align.FILL);
- combo.set_hexpand (true);
- this.add (combo);
-
- var renderer = new CellRendererText ();
- combo.pack_start (renderer, true);
- combo.set_attributes (renderer,
- "text", 0);
- combo.set_row_separator_func ( (model, iter) => {
- string? s;
- model.get (iter, 0, out s);
- return s == null;
- });
-
- entry = new Entry ();
- entry.set_halign (Align.FILL);
- entry.set_hexpand (true);
- // Make the default entry small so we don't unnecessarily
- // expand the labels (it'll be expanded a bit anyway)
- entry.width_chars = 4;
-
- this.add (entry);
-
- combo.set_no_show_all (true);
- entry.set_no_show_all (true);
-
- combo.show ();
-
- combo.changed.connect (combo_changed);
- entry.focus_out_event.connect (entry_focus_out_event);
- entry.activate.connect (entry_activate);
- entry.key_release_event.connect (entry_key_release);
- }
-
- private void finish_custom () {
- if (!custom_mode)
- return;
-
- custom_mode = false;
- var text = entry.get_text ();
-
- if (text != "") {
- TreeIter iter;
- type_set.add_custom_label (text, out iter);
-
- last_active = iter;
- combo.set_active_iter (iter);
- } else {
- combo.set_active_iter (last_active);
- }
-
- combo.show ();
- entry.hide ();
- }
-
- private void entry_activate () {
- finish_custom ();
- }
-
- private bool entry_key_release (Gdk.EventKey event) {
- if (event.keyval == Gdk.Key.Escape) {
- entry.set_text ("");
- finish_custom ();
- }
- return true;
- }
-
- private bool entry_focus_out_event (Gdk.EventFocus event) {
- finish_custom ();
- return false;
- }
-
- private void combo_changed (ComboBox combo) {
- if (in_manual_change)
- return;
-
- 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 ();
- }
- }
- }
-
- private void set_from_iter (TreeIter iter) {
- in_manual_change = true;
- last_active = iter;
- combo.set_active_iter (iter);
- in_manual_change = false;
- modified = false;
- }
-
- public void set_active (AbstractFieldDetails details) {
- TreeIter iter;
- type_set.lookup_type (details, out iter);
- set_from_iter (iter);
- }
-
- public void set_to (string type) {
- TreeIter iter;
- type_set.lookup_type_by_string (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);
- }
-}
diff --git a/src/contacts-typeset.vala b/src/contacts-typeset.vala
new file mode 100644
index 0000000..8b6329c
--- /dev/null
+++ b/src/contacts-typeset.vala
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl@redhat.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 Gtk;
+using Gee;
+using Folks;
+
+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<InitData*> init_data;
+ public TreeIter iter; // Set if in_store
+ public bool in_store;
+ }
+
+ // 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<unowned string, TypeDescriptor> display_name_hash;
+ // Map from all type strings in to list of InitData with the data in it
+ private HashTable<unowned string, GLib.List<InitData*> > vcard_lookup_hash;
+ // Map from display name to TreeIter for all custom types
+ private HashTable<string, TreeIter?> custom_hash;
+
+ public Gtk.ListStore store;
+ private TreeIter other_iter;
+
+ private TypeSet () {
+ display_name_hash = new HashTable<unowned string, TypeDescriptor> (str_hash, str_equal);
+ vcard_lookup_hash = new HashTable<unowned string, GLib.List<InitData*> > (str_hash, str_equal);
+ custom_hash = new HashTable<string, TreeIter? > (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 void add_descriptor_to_store (TypeDescriptor descriptor, bool is_custom) {
+ if (descriptor.in_store)
+ return;
+
+ descriptor.in_store = true;
+ if (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 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);
+ }
+
+ 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<InitData*> l = this.vcard_lookup_hash.lookup (type);
+ if (l != null) {
+ l.append (init_data);
+ } else {
+ GLib.List<InitData*> l2 = null;
+ l2.append (init_data);
+ this.vcard_lookup_hash.insert (type, (owned) l2);
+ }
+ }
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+
+ unowned TreeIter? iterp = custom_hash.lookup (label);
+ if (iterp != null) {
+ iter = iterp;
+ return;
+ }
+
+ store.insert_before (out iter, null);
+ store.set (iter, 0, label, 1, null);
+ custom_hash.insert (label, iter);
+ }
+
+ private unowned TypeDescriptor? lookup_descriptor_by_string (string str) {
+ unowned GLib.List<InitData *>? 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);
+ }
+ }
+
+ return null;
+ }
+
+
+ private unowned TypeDescriptor? lookup_descriptor (AbstractFieldDetails detail) {
+ var i = detail.get_parameter_values ("type");
+ if (i == null || i.is_empty)
+ return null;
+
+ var list = new Gee.ArrayList<string> ();
+ 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<InitData *>? 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);
+ }
+ }
+
+ 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<string> (detail.parameters.get (X_GOOGLE_LABEL));
+ add_custom_label (label, out iter);
+ return;
+ }
+
+ unowned TypeDescriptor? d = lookup_descriptor (detail);
+ if (d != null) {
+ add_descriptor_to_store (d, true);
+ iter = d.iter;
+ } else {
+ iter = other_iter;
+ }
+ }
+
+ 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;
+ }
+
+ public string format_type (AbstractFieldDetails detail) {
+ if (detail.parameters.contains (X_GOOGLE_LABEL))
+ return Utils.get_first<string> (detail.parameters[X_GOOGLE_LABEL]);
+
+ unowned TypeDescriptor? d = lookup_descriptor (detail);
+ return (d != null)? d.display_name : _("Other");
+ }
+
+ public void update_details (AbstractFieldDetails details, TreeIter iter) {
+ var old_parameters = details.parameters;
+ details.parameters = new HashMultiMap<string, string> ();
+ bool has_pref = false;
+ foreach (var val in old_parameters["type"]) {
+ if (val.ascii_casecmp ("PREF") == 0) {
+ has_pref = true;
+ break;
+ }
+ }
+ 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;
+ }
+
+ 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];
+ }
+ }
+
+ if (has_pref)
+ details.parameters["type"] = "PREF";
+ }
+
+ 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 = {
+ // 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 ();
+ for (int i = 0; i < general_data.length; i++)
+ _general.add_init_data (&general_data[i]);
+ _general.add_init_data_done (standard);
+ }
+
+ return _general;
+ }
+ }
+
+ private static TypeSet _email;
+ private const InitData[] email_data = {
+ // List most specific first, always in upper case
+ { N_("Personal"), { "PERSONAL" } },
+ { N_("Home"), { "HOME" } },
+ { N_("Work"), { "WORK" } }
+ };
+ public static TypeSet email {
+ get {
+ string[] standard = {
+ "Personal", "Home", "Work"
+ };
+
+ if (_email == null) {
+ _email = new TypeSet ();
+ for (int i = 0; i < email_data.length; i++)
+ _email.add_init_data (&email_data[i]);
+ _email.add_init_data_done (standard);
+ }
+
+ return _email;
+ }
+ }
+
+ private static TypeSet _phone;
+ 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);
+ }
+
+ return _phone;
+ }
+ }
+}
diff --git a/src/meson.build b/src/meson.build
index d471144..b09fea5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -10,7 +10,7 @@ libcontacts_sources = [
'contacts-esd-setup.vala',
'contacts-im-service.vala',
'contacts-store.vala',
- 'contacts-types.vala',
+ 'contacts-typeset.vala',
'contacts-utils.vala',
]
@@ -87,6 +87,7 @@ contacts_vala_sources = files(
'contacts-max-width-bin.vala',
'contacts-settings.vala',
'contacts-setup-window.vala',
+ 'contacts-type-combo.vala',
'contacts-ui-state.vala',
'contacts-window.vala',
'main.vala',