[gnome-contacts] Initial work on TypeCombo widget



commit 2574080b1f994b29a5f00cb3a26121bd769387bf
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Jun 22 12:04:25 2011 +0200

    Initial work on TypeCombo widget

 src/contacts-contact-pane.vala |   12 ++-
 src/contacts-types.vala        |  163 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 158 insertions(+), 17 deletions(-)
---
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 7bc56b3..c8f3db9 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -210,8 +210,10 @@ public class Contacts.ContactPane : EventBox {
       var emails = email_details.email_addresses;
       if (!emails.is_empty) {
 	foreach (var email in Contact.sort_fields (emails)) {
-	  var type = TypeSet.general.format_type (email);
-	  layout.add_label_detail (type, email.value);
+	  var combo = new TypeCombo (TypeSet.general);
+	  combo.set_active (email);
+	  layout.add_widget_label (combo);
+	  layout.add_detail (email.value);
 	}
       }
     }
@@ -235,8 +237,10 @@ public class Contacts.ContactPane : EventBox {
       var phone_numbers = phone_details.phone_numbers;
       if (!phone_numbers.is_empty) {
 	foreach (var p in Contact.sort_fields (phone_numbers)) {
-	  var type = TypeSet.phone.format_type (p);
-	  layout.add_label_detail (type, p.value);
+	  var combo = new TypeCombo (TypeSet.phone);
+	  combo.set_active (p);
+	  layout.add_widget_label (combo);
+	  layout.add_detail (p.value);
 	}
       }
     }
diff --git a/src/contacts-types.vala b/src/contacts-types.vala
index 673305f..f59bc07 100644
--- a/src/contacts-types.vala
+++ b/src/contacts-types.vala
@@ -22,33 +22,137 @@ using Gee;
 using Folks;
 
 public class Contacts.TypeCombo : Grid  {
+  TypeSet type_set;
+  ComboBox combo;
+  Entry entry;
+  bool custom_mode;
 
+  public TypeCombo (TypeSet type_set) {
+    this.type_set = type_set;
+
+    combo = new ComboBox.with_model (type_set.store);
+    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.width_chars = 10;
+
+    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);
+  }
+
+  private void finish_custom () {
+    if (!custom_mode)
+      return;
+
+    custom_mode = false;
+    var text = entry.get_text ();
+
+    TreeIter iter;
+    type_set.add_custom_label (text, out iter);
+
+    combo.set_active_iter (iter);
+
+    combo.show ();
+    entry.hide ();
+  }
+
+  private void entry_activate () {
+    finish_custom ();
+  }
+
+  private bool entry_focus_out_event (Gdk.EventFocus event) {
+    finish_custom ();
+    return false;
+  }
+
+  private void combo_changed (ComboBox combo) {
+    TreeIter iter;
+    if (combo.get_active_iter (out iter) &&
+	type_set.is_custom (iter)) {
+      custom_mode = true;
+      combo.hide ();
+      entry.show ();
+      entry.grab_focus ();
+    }
+  }
+
+  public void set_active (FieldDetails details) {
+    TreeIter iter;
+    type_set.lookup_detail (details, out iter);
+    combo.set_active_iter (iter);
+  }
 }
 
 public class Contacts.TypeSet : Object  {
   const int MAX_TYPES = 3;
+  private struct Data {
+    InitData *init_data;
+    TreeIter iter;
+  }
   private struct InitData {
     unowned string display_name;
     unowned string types[3]; //MAX_TYPES
   }
 
-  private static HashTable<unowned string, GLib.List<InitData *> > hash;
+  static InitData custom_dummy;
+
+  private HashTable<unowned string, GLib.List<Data?> > hash;
+  private HashTable<unowned string, TreeIter?> custom_hash;
+  public ListStore store;
+  private TreeIter other_iter;
+  private TreeIter custom_iter;
 
   private TypeSet () {
-    hash = new HashTable<unowned string, GLib.List<InitData*> > (str_hash, str_equal);
+    hash = new HashTable<unowned string, GLib.List<Data?> > (str_hash, str_equal);
+    custom_hash = new HashTable<unowned string, TreeIter? > (str_hash, str_equal);
+    store = new ListStore (2, typeof(string?), typeof (InitData *));
   }
 
-  private void add_data (InitData *data) {
-    unowned GLib.List<InitData *> l = hash.lookup (data.types[0]);
+  private void add_data (InitData *init_data) {
+    Data data = Data();
+    data.init_data = init_data;
+    store.append (out data.iter);
+    store.set (data.iter, 0, dgettext (Config.GETTEXT_PACKAGE, init_data.display_name), 1, data);
+
+    unowned GLib.List<Data?> l = hash.lookup (init_data.types[0]);
     if (l != null) {
       l.append (data);
     } else {
-      GLib.List<InitData *> l2 = null;
+      GLib.List<Data?> l2 = null;
       l2.append (data);
-      hash.insert (data.types[0], (owned) l2);
+      hash.insert (init_data.types[0], (owned) l2);
     }
   }
 
+  private void add_data_done () {
+    store.append (out other_iter);
+    store.set (other_iter, 0, _("Other"), 1, null);
+
+    TreeIter iter;
+    store.append (out iter);
+    store.set (iter, 0, null);
+    store.append (out custom_iter);
+    store.set (custom_iter, 0, _("Custom..."), 1, custom_dummy);
+  }
+
   private static TypeSet _general;
   private static TypeSet _phone;
 
@@ -65,6 +169,7 @@ public class Contacts.TypeSet : Object  {
 	for (int i = 0; i < data.length; i++) {
 	  _general.add_data (&data[i]);
 	}
+	_general.add_data_done ();
       }
 
       return _general;
@@ -103,6 +208,7 @@ public class Contacts.TypeSet : Object  {
 	for (int i = 0; i < data.length; i++) {
 	  _phone.add_data (&data[i]);
 	}
+	_phone.add_data_done ();
       }
 
       return _phone;
@@ -123,7 +229,7 @@ public class Contacts.TypeSet : Object  {
     return int.parse (s);
   }
 
-  public InitData *lookup_data (FieldDetails detail) {
+  private unowned Data? lookup_data (FieldDetails detail) {
     var i = detail.get_parameter_values ("type");
     if (i == null || i.is_empty)
       return null;
@@ -142,11 +248,11 @@ public class Contacts.TypeSet : Object  {
 
     list.sort ();
 
-    unowned GLib.List<InitData *>? l = hash.lookup (list[0]);
-    foreach (var d in l) {
+    unowned GLib.List<Data?>? l = hash.lookup (list[0]);
+    foreach (unowned Data? 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])) {
+      for (int j = 0; j < MAX_TYPES && d.init_data.types[j] != null; j++) {
+	if (!list.contains (d.init_data.types[j])) {
 	  all_found = false;
 	  break;
 	}
@@ -163,11 +269,42 @@ public class Contacts.TypeSet : Object  {
       return get_first_string (detail.parameters.get ("x-google-label"));
     }
 
-    var d = lookup_data (detail);
+    unowned Data? d = lookup_data (detail);
     if (d != null) {
-      return dgettext (Config.GETTEXT_PACKAGE, d.display_name);
+      return dgettext (Config.GETTEXT_PACKAGE, d.init_data.display_name);
     }
 
     return _("Other");
   }
+
+  public void lookup_detail (FieldDetails detail, out TreeIter iter) {
+    if (detail.parameters.contains ("x-google-label")) {
+      var label = 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)
+      iter = d.iter;
+    else
+      iter = other_iter;
+  }
+
+  public bool is_custom (TreeIter iter) {
+    InitData *data;
+    store.get (iter, 1, out data);
+    return data == &custom_dummy;
+  }
+
+  public void add_custom_label (string label, out TreeIter iter) {
+    unowned TreeIter? iterp = custom_hash.lookup (label);
+    if (iterp != null) {
+      iter = iterp;
+      return;
+    }
+    store.insert_before (out iter, custom_iter);
+    store.set (iter, 0, label, 1, null);
+    custom_hash.insert (label, iter);
+  }
 }



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