[gnome-contacts] Split out the view from Store



commit d62e0780ac63a5202faffc0c8863a6a921d470e6
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Aug 17 11:49:47 2011 +0200

    Split out the view from Store
    
    This way we can allow multiple views of the same store, with
    different filtering.

 src/Makefile.am             |    1 +
 src/contacts-contact.vala   |   13 ++-
 src/contacts-list-pane.vala |   12 ++-
 src/contacts-store.vala     |  233 ++++--------------------------------------
 src/contacts-view.vala      |  239 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 274 insertions(+), 224 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 663e58b..805a26a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,7 @@ gnome_contacts_SOURCES = \
 	contacts-list-pane.vala \
 	contacts-menu-button.vala \
 	contacts-store.vala \
+	contacts-view.vala \
 	contacts-utils.vala \
 	main.vala \
 	$(NULL)
diff --git a/src/contacts-contact.vala b/src/contacts-contact.vala
index ec0a1a7..97ac7b8 100644
--- a/src/contacts-contact.vala
+++ b/src/contacts-contact.vala
@@ -183,26 +183,27 @@ public class Contacts.Contact : GLib.Object  {
       SignalHandler.disconnect_by_func (tp.contact, (void *)persona_notify_cb, this);
   }
 
-  public void *lookup (void *key) {
-    foreach (var data_ref in refs) {
+  public unowned T lookup<T> (void *key) {
+    foreach (unowned ContactDataRef? data_ref in refs) {
       if (data_ref.key == key)
-	return data_ref.data;
+	return (T*)data_ref.data;
     }
     return null;
   }
 
-  public void set_lookup (void *key, void *data) {
+  public void set_lookup<T> (void *key, owned T data) {
     int i = refs.length;
     refs.resize(i+1);
     refs[i].key = key;
-    refs[i].data = data;
+    refs[i].data = (void *)(owned)data;
   }
 
-  public void remove_lookup (void *key) {
+  public void remove_lookup<T> (void *key) {
     int i;
 
     for (i = 0; i < refs.length; i++) {
       if (refs[i].key == key) {
+	T old_val = (owned)refs[i].data;
 	for (int j = i + 1; j < refs.length; j++) {
 	  refs[j-1] = refs[j];
 	}
diff --git a/src/contacts-list-pane.vala b/src/contacts-list-pane.vala
index a943252..a013098 100644
--- a/src/contacts-list-pane.vala
+++ b/src/contacts-list-pane.vala
@@ -299,6 +299,7 @@ public class Contacts.CellRendererShape : Gtk.CellRenderer {
 
 public class Contacts.ListPane : Frame {
   private Store contacts_store;
+  private View contacts_view;
   private TreeView contacts_tree_view;
   public Entry filter_entry;
   private uint filter_entry_changed_id;
@@ -326,7 +327,7 @@ public class Contacts.ListPane : Frame {
 	model.get (iter, 0, out contact);
 
 	string letter = "";
-	if (contacts_store.is_first (iter)) {
+	if (contacts_view.is_first (iter)) {
 	  letter = contact.initial_letter.to_string ();
 	}
 	cell.set ("text", letter);
@@ -377,7 +378,7 @@ public class Contacts.ListPane : Frame {
       values = str.split(" ");
     }
 
-    contacts_store.set_filter_values (values);
+    contacts_view.set_filter_values (values);
   }
 
   private bool filter_entry_changed_timeout () {
@@ -416,6 +417,7 @@ public class Contacts.ListPane : Frame {
 
   public ListPane (Store contacts_store) {
     this.contacts_store = contacts_store;
+    this.contacts_view = new View (contacts_store);
     var toolbar = new Toolbar ();
     toolbar.get_style_context ().add_class (STYLE_CLASS_PRIMARY_TOOLBAR);
     toolbar.set_icon_size (IconSize.MENU);
@@ -465,7 +467,7 @@ public class Contacts.ListPane : Frame {
     grid.attach (toolbar, 0, 0, 1, 1);
     grid.attach (scrolled, 0, 1, 1, 1);
 
-    contacts_tree_view = new TreeView.with_model (contacts_store.model);
+    contacts_tree_view = new TreeView.with_model (contacts_view.model);
     setup_contacts_view (contacts_tree_view);
     scrolled.add (contacts_tree_view);
 
@@ -474,9 +476,9 @@ public class Contacts.ListPane : Frame {
 
   public void select_contact (Contact contact) {
     TreeIter iter;
-    if (contacts_store.lookup_iter (contact, out iter)) {
+    if (contacts_view.lookup_iter (contact, out iter)) {
       contacts_tree_view.get_selection ().select_iter (iter);
-      contacts_tree_view.scroll_to_cell (contacts_store.model.get_path (iter),
+      contacts_tree_view.scroll_to_cell (contacts_view.model.get_path (iter),
 					 null, true, 0.0f, 0.0f);
     }
   }
diff --git a/src/contacts-store.vala b/src/contacts-store.vala
index e3e886e..7c2a70a 100644
--- a/src/contacts-store.vala
+++ b/src/contacts-store.vala
@@ -19,35 +19,18 @@
 
 using Gtk;
 using Folks;
+using Gee;
 
-public class Contacts.Store  {
-  private class ContactData {
-    public Contact contact;
-    public TreeIter iter;
-    public bool visible;
-    public bool is_first;
-  }
-
+public class Contacts.Store : GLib.Object {
   public signal void changed (Contact c);
   public signal void added (Contact c);
   public signal void removed (Contact c);
 
-  ListStore list_store;
   public IndividualAggregator aggregator { get; private set; }
-  Gee.ArrayList<ContactData> contacts;
-  string []? filter_values;
+  Gee.ArrayList<Contact> contacts;
 
   public Store () {
-    list_store = new ListStore (2, typeof (Contact), typeof (ContactData *));
-    contacts = new Gee.ArrayList<ContactData>();
-
-    list_store.set_sort_func (0, (model, iter_a, iter_b) => {
-	Contact a, b;
-	model.get (iter_a, 0, out a);
-	model.get (iter_b, 0, out b);
-	return a.display_name.collate (b.display_name);
-      });
-    list_store.set_sort_column_id (0, SortType.ASCENDING);
+    contacts = new Gee.ArrayList<Contact>();
 
     aggregator = new IndividualAggregator ();
     aggregator.individuals_changed.connect ((added, removed, m, a, r) =>   {
@@ -62,228 +45,52 @@ public class Contacts.Store  {
     aggregator.prepare ();
   }
 
-  public TreeModel model { get { return list_store; } }
-
-  private bool apply_filter (Contact contact) {
-    // Don't show the user itself
-    if (contact.individual.is_user)
-      return false;
-
-    var personas = contact.individual.personas;
-    var i = personas.iterator();
-    // Look for single-persona individuals
-    if (i.next() && !i.has_next ()) {
-      var persona = i.get();
-      var store = persona.store;
-
-      // Filter out pure key-file persona individuals as these are
-      // not very interesting
-      if (store.type_id == "key-file")
-	return false;
-
-      // Filter out uncertain things like link-local xmpp
-      if (store.type_id == "telepathy" &&
-	  store.trust_level == PersonaStoreTrust.NONE)
-	return false;
-    }
-
-    if (filter_values == null || filter_values.length == 0)
-      return true;
-
-    return contact.contains_strings (filter_values);
-  }
-
-  public bool is_first (TreeIter iter) {
-    ContactData *data;
-    list_store.get (iter, 1, out data);
-    if (data != null)
-      return data->is_first;
-    return false;
-  }
-
-  private ContactData? get_previous (ContactData data) {
-    ContactData *previous = null;
-    TreeIter iter = data.iter;
-    if (list_store.iter_previous (ref iter))
-      list_store.get (iter, 1, out previous);
-    return previous;
-  }
-
-  private ContactData? get_next (ContactData data) {
-    ContactData *next = null;
-    TreeIter iter = data.iter;
-    if (list_store.iter_next (ref iter))
-      list_store.get (iter, 1, out next);
-    return next;
-  }
-
-  private void row_changed_no_resort (ContactData data) {
-    var path = list_store.get_path (data.iter);
-    list_store.row_changed (path, data.iter);
-  }
-
-  private void row_changed_resort (ContactData data) {
-    list_store.set (data.iter, 0, data.contact);
-  }
-
-  private bool update_is_first (ContactData data, ContactData? previous) {
-    bool old_is_first = data.is_first;
-
-    if (previous != null) {
-      unichar previous_initial = previous.contact.initial_letter;
-      unichar initial = data.contact.initial_letter;
-      data.is_first = previous_initial != initial;
-    } else {
-      data.is_first = true;
-    }
-
-    if (old_is_first != data.is_first) {
-      row_changed_no_resort (data);
-      return true;
-    }
-
-    return false;
-  }
-
-  private void add_to_model (ContactData data) {
-    list_store.append (out data.iter);
-    list_store.set (data.iter, 0, data.contact, 1, data);
-
-    if (update_is_first (data, get_previous (data)) && data.is_first) {
-      /* The newly added row is first, the next one might not be anymore */
-      var next = get_next (data);
-      if (next != null)
-	update_is_first (next, data);
-    }
-  }
-
-  private void remove_from_model (ContactData data) {
-    ContactData? next = null;
-    if (data.is_first)
-      next = get_next (data);
-
-    list_store.remove (data.iter);
-    data.is_first = false;
-
-    if (next != null)
-      update_is_first (next, get_previous (next));
-  }
-
-  private void update_visible (ContactData data) {
-    bool was_visible = data.visible;
-    data.visible = apply_filter (data.contact);
-
-    if (!was_visible && data.visible)
-      add_to_model (data);
-
-    if (was_visible && !data.visible)
-      remove_from_model (data);
-  }
-
-  private void refilter () {
-    foreach (var d in contacts) {
-      update_visible (d);
-    }
-  }
-
-  public void set_filter_values (string []? values) {
-    filter_values = values;
-    refilter ();
-  }
-
   private void contact_changed_cb (Contact c) {
-    ContactData data = lookup_data (c);
-
-    bool was_visible = data.visible;
-
-    ContactData? next = null;
-    if (data.visible)
-      next = get_next (data);
-
-    update_visible (data);
-
-    if (was_visible && data.visible) {
-      /* We just moved position in the list while visible */
-
-      row_changed_resort (data);
-
-      /* Update the is_first on the previous next row */
-      if (next != null)
-	update_is_first (next, get_previous (next));
-
-      /* Update the is_first on the new next row */
-      next = get_next (data);
-      if (next != null)
-	update_is_first (next, data);
-    }
-
     changed (c);
   }
 
-  private ContactData lookup_data (Contact c) {
-    return (ContactData *)c.lookup (this);
-  }
-
-  public bool lookup_iter (Contact c, out TreeIter iter) {
-    var data = lookup_data (c);
-    iter = data.iter;
-    return data.visible;
-  }
-
   public Contact? find_contact_with_id (string individual_id) {
-    foreach (var data in contacts) {
-      if (data.contact.individual.id == individual_id)
-	return data.contact;
+    foreach (var contact in contacts) {
+      if (contact.individual.id == individual_id)
+	return contact;
     }
     return null;
   }
 
   public Contact? find_contact_with_email (string email_address) {
-    foreach (var data in contacts) {
-      if (data.contact.has_email (email_address))
-	return data.contact;
+    foreach (var contact in contacts) {
+      if (contact.has_email (email_address))
+	return contact;
     }
     return null;
   }
 
   public Contact? find_contact_with_persona (Persona persona) {
-    foreach (var data in contacts) {
-      if (data.contact.individual.personas.contains (persona))
-	return data.contact;
+    foreach (var contact in contacts) {
+      if (contact.individual.personas.contains (persona))
+	return contact;
     }
     return null;
   }
 
-  public void add (Contact c) {
-    ContactData data =  new ContactData();
-    data.contact = c;
-    data.visible = false;
-
-    c.set_lookup (this, data);
-
-    contacts.add (data);
+  public Collection<Contact> get_contacts () {
+    return contacts.read_only_view;
+  }
 
+  private void add (Contact c) {
+    contacts.add (c);
     c.changed.connect (contact_changed_cb);
-
-    update_visible (data);
-
     added (c);
   }
 
-  public void remove (Contact c) {
+  private void remove (Contact c) {
     c.changed.disconnect (contact_changed_cb);
-    var data = lookup_data (c);
 
-    if (data.visible)
-      remove_from_model (data);
-
-    var i = contacts.index_of (data);
+    var i = contacts.index_of (c);
     if (i != contacts.size - 1)
       contacts.set (i, contacts.get (contacts.size - 1));
     contacts.remove_at (contacts.size - 1);
 
-    c.remove_lookup (this);
-
     removed (c);
   }
 }
diff --git a/src/contacts-view.vala b/src/contacts-view.vala
new file mode 100644
index 0000000..4e98aab
--- /dev/null
+++ b/src/contacts-view.vala
@@ -0,0 +1,239 @@
+/* -*- 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+using Gtk;
+using Folks;
+
+public class Contacts.View : GLib.Object {
+  private class ContactData {
+    public Contact contact;
+    public TreeIter iter;
+    public bool visible;
+    public bool is_first;
+  }
+
+  Store contacts_store;
+  ListStore list_store;
+  string []? filter_values;
+
+  public View (Store store) {
+    contacts_store = store;
+
+    list_store = new ListStore (2, typeof (Contact), typeof (ContactData *));
+
+    list_store.set_sort_func (0, (model, iter_a, iter_b) => {
+	Contact a, b;
+	model.get (iter_a, 0, out a);
+	model.get (iter_b, 0, out b);
+	return a.display_name.collate (b.display_name);
+      });
+    list_store.set_sort_column_id (0, SortType.ASCENDING);
+
+    contacts_store.added.connect (contact_added_cb);
+    contacts_store.removed.connect (contact_removed_cb);
+    contacts_store.changed.connect (contact_changed_cb);
+    foreach (var c in store.get_contacts ())
+      contact_added_cb (store, c);
+  }
+
+  public TreeModel model { get { return list_store; } }
+
+  private bool apply_filter (Contact contact) {
+    // Don't show the user itself
+    if (contact.individual.is_user)
+      return false;
+
+    var personas = contact.individual.personas;
+    var i = personas.iterator();
+    // Look for single-persona individuals
+    if (i.next() && !i.has_next ()) {
+      var persona = i.get();
+      var store = persona.store;
+
+      // Filter out pure key-file persona individuals as these are
+      // not very interesting
+      if (store.type_id == "key-file")
+	return false;
+
+      // Filter out uncertain things like link-local xmpp
+      if (store.type_id == "telepathy" &&
+	  store.trust_level == PersonaStoreTrust.NONE)
+	return false;
+    }
+
+    if (filter_values == null || filter_values.length == 0)
+      return true;
+
+    return contact.contains_strings (filter_values);
+  }
+
+  public bool is_first (TreeIter iter) {
+    ContactData *data;
+    list_store.get (iter, 1, out data);
+    if (data != null)
+      return data->is_first;
+    return false;
+  }
+
+  private ContactData? get_previous (ContactData data) {
+    ContactData *previous = null;
+    TreeIter iter = data.iter;
+    if (list_store.iter_previous (ref iter))
+      list_store.get (iter, 1, out previous);
+    return previous;
+  }
+
+  private ContactData? get_next (ContactData data) {
+    ContactData *next = null;
+    TreeIter iter = data.iter;
+    if (list_store.iter_next (ref iter))
+      list_store.get (iter, 1, out next);
+    return next;
+  }
+
+  private void row_changed_no_resort (ContactData data) {
+    var path = list_store.get_path (data.iter);
+    list_store.row_changed (path, data.iter);
+  }
+
+  private void row_changed_resort (ContactData data) {
+    list_store.set (data.iter, 0, data.contact);
+  }
+
+  private bool update_is_first (ContactData data, ContactData? previous) {
+    bool old_is_first = data.is_first;
+
+    if (previous != null) {
+      unichar previous_initial = previous.contact.initial_letter;
+      unichar initial = data.contact.initial_letter;
+      data.is_first = previous_initial != initial;
+    } else {
+      data.is_first = true;
+    }
+
+    if (old_is_first != data.is_first) {
+      row_changed_no_resort (data);
+      return true;
+    }
+
+    return false;
+  }
+
+  private void add_to_model (ContactData data) {
+    list_store.append (out data.iter);
+    list_store.set (data.iter, 0, data.contact, 1, data);
+
+    if (update_is_first (data, get_previous (data)) && data.is_first) {
+      /* The newly added row is first, the next one might not be anymore */
+      var next = get_next (data);
+      if (next != null)
+	update_is_first (next, data);
+    }
+  }
+
+  private void remove_from_model (ContactData data) {
+    ContactData? next = null;
+    if (data.is_first)
+      next = get_next (data);
+
+    list_store.remove (data.iter);
+    data.is_first = false;
+
+    if (next != null)
+      update_is_first (next, get_previous (next));
+  }
+
+  private void update_visible (ContactData data) {
+    bool was_visible = data.visible;
+    data.visible = apply_filter (data.contact);
+
+    if (!was_visible && data.visible)
+      add_to_model (data);
+
+    if (was_visible && !data.visible)
+      remove_from_model (data);
+  }
+
+  private void refilter () {
+    foreach (var c in contacts_store.get_contacts ()) {
+      update_visible (lookup_data (c));
+    }
+  }
+
+  public void set_filter_values (string []? values) {
+    filter_values = values;
+    refilter ();
+  }
+
+  private void contact_changed_cb (Store store, Contact c) {
+    ContactData data = lookup_data (c);
+
+    bool was_visible = data.visible;
+
+    ContactData? next = null;
+    if (data.visible)
+      next = get_next (data);
+
+    update_visible (data);
+
+    if (was_visible && data.visible) {
+      /* We just moved position in the list while visible */
+
+      row_changed_resort (data);
+
+      /* Update the is_first on the previous next row */
+      if (next != null)
+	update_is_first (next, get_previous (next));
+
+      /* Update the is_first on the new next row */
+      next = get_next (data);
+      if (next != null)
+	update_is_first (next, data);
+    }
+  }
+
+  private ContactData lookup_data (Contact c) {
+    return c.lookup<ContactData> (this);
+  }
+
+  private void contact_added_cb (Store store, Contact c) {
+    ContactData data =  new ContactData();
+    data.contact = c;
+    data.visible = false;
+
+    c.set_lookup (this, data);
+
+    update_visible (data);
+  }
+
+  private void contact_removed_cb (Store store, Contact c) {
+    var data = lookup_data (c);
+
+    if (data.visible)
+      remove_from_model (data);
+
+    c.remove_lookup<ContactData> (this);
+  }
+
+  public bool lookup_iter (Contact c, out TreeIter iter) {
+    var data = lookup_data (c);
+    iter = data.iter;
+    return data.visible;
+  }
+}



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