[gnome-contacts] Split out the view from Store
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts] Split out the view from Store
- Date: Wed, 17 Aug 2011 15:47:50 +0000 (UTC)
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]