diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2010-08-19 00:07:09 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2010-08-19 00:07:09 +0200 |
commit | 57a0ca523f0e3a2aa4355636b7ccf60d33f8aaf7 (patch) | |
tree | 28fd4791f5cf93a716197c31e919466fddf1daa1 /src/gui/ContactList.vala | |
parent | acd7d39f7fa7d868d95abda956347cd8496b32e0 (diff) | |
download | ephraim-57a0ca523f0e3a2aa4355636b7ccf60d33f8aaf7.tar ephraim-57a0ca523f0e3a2aa4355636b7ccf60d33f8aaf7.zip |
Renamed Roster class to ContactList as it will also be used for the MUC participant list
Diffstat (limited to 'src/gui/ContactList.vala')
-rw-r--r-- | src/gui/ContactList.vala | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/src/gui/ContactList.vala b/src/gui/ContactList.vala new file mode 100644 index 0000000..8696f93 --- /dev/null +++ b/src/gui/ContactList.vala @@ -0,0 +1,729 @@ +public class ContactList { + private Gtk.TreeView contactView; + private ContactListModel contactList; + private ContactGroupModel model; + private Gee.Map<string, Gee.Map<string, Contact>> groups = new Gee.HashMap<string, Gee.Map<string, Contact>>(); + + public signal void start_conversation(string jid); + + public ContactList(Gtk.TreeView contactView0) { + contactView = contactView0; + contactList = new ContactListModel(this); + model = new ContactGroupModel(contactList); + + contactView.set_model(new ContactListFilter(this, new ContactListSorter(model))); + + contactView.query_tooltip.connect((x, y, keyboard_tip, tooltip) => { + Gtk.TreeModel model; + Gtk.TreeIter iter; + + if(!contactView.get_tooltip_context(out x, out y, keyboard_tip, out model, null, out iter)) + return false; + + Value value; + model.get_value(iter, 0, out value); + + Contact? c = value.get_object() as Contact; + if(c == null) + return false; + + Gee.Map.Entry<string, Contact.Resource>? r = c.get_resource_with_highest_priority(); + + if(r == null) + return false; + + tooltip.set_text("Resource: " + r.key + " (" + r.value.priority.to_string() + ")"); + + return true; + }); + contactView.has_tooltip = true; + + contactView.row_activated.connect((view, path, column) => { + Gtk.TreeIter iter; + view.model.get_iter(out iter, path); + + Value value; + view.model.get_value(iter, 0, out value); + + Contact contact = value.get_object() as Contact; + if(contact == null) + return; + + Gee.Map.Entry<string, Contact.Resource> res = contact.get_resource_with_highest_priority(); + if(res == null) + return; //FIXME + + start_conversation(contact.jid + "/" + res.key); + }); + + contactView.model.row_has_child_toggled.connect((path, iter) => { + contactView.expand_row(path, true); + }); + + Gtk.TreeViewColumn contactColumn = new Gtk.TreeViewColumn.with_attributes(null, new CellRendererContact(), "data", 0, null); + contactView.append_column(contactColumn); + } + + public void update_contact(Contact c) { + contactList.update_contact(c); + } + + public Contact get_contact(string jid) { + return contactList.get_contact(jid); + } + + private void update_groups() { + foreach(Gee.Map.Entry<string, Gee.Map<string, Contact>> group in groups) { + if(group.value.is_empty) { + model.remove_group(group.key); + groups.unset(group.key); + } + else { + model.add_group(group.key); + } + } + } + + public Gee.Map<string, Contact> get_group(string group) { + return groups[group]; + } + + private class ContactGroupModel : Object, Gtk.TreeModel { + private int stamp = 0; + private Gee.Map<String, Gtk.TreeModel> groupModels = new Gee.TreeMap<String, Gtk.TreeModel>(); + private Gee.ArrayList<Gtk.TreeIter?> childIters = new Gee.ArrayList<Gtk.TreeIter?>(); + private ContactListModel contactList; + + + private void incrementStamp() { + stamp++; + childIters.clear(); + } + + public ContactGroupModel(ContactListModel contactList0) { + contactList = contactList0; + } + + public void add_group(string name) { + int index = 0; + foreach(String group in groupModels.keys) { + if(group.data == name) { + Gtk.TreePath path = new Gtk.TreePath.from_indices(index, -1); + Gtk.TreeIter iter; + get_iter(out iter, path); + row_changed(path, iter); + + return; + } + + index++; + } + + Gtk.TreeModel model = new GroupFilter(contactList, name); + + model.row_changed.connect((subpath, subiter) => { + Gtk.TreePath path = child_path_to_path(name, subpath); + Gtk.TreeIter iter; + get_iter(out iter, path); + row_changed(path, iter); + }); + + model.row_deleted.connect((path) => { + path.prepend_index(0); + row_deleted(path); + + Gtk.TreePath parent = path.copy(); + parent.up(); + if(parent.get_depth() == 1) { + Gtk.TreeIter iter; + get_iter(out iter, parent); + if(!iter_has_child(iter)) { + row_has_child_toggled(parent, iter); + } + } + }); + + model.row_has_child_toggled.connect((subpath, subiter) => { + Gtk.TreePath path = child_path_to_path(name, subpath); + Gtk.TreeIter iter; + get_iter(out iter, path); + row_has_child_toggled(path, iter); + }); + + model.row_inserted.connect((subpath, subiter) => { + Gtk.TreePath path = child_path_to_path(name, subpath); + + incrementStamp(); + Gtk.TreeIter iter; + get_iter(out iter, path); + row_inserted(path, iter); + + Gtk.TreePath parent = path.copy(); + parent.up(); + if(parent.get_depth() == 1) { + get_iter(out iter, parent); + Value value; + get_value(iter, 0, out value); + if(iter_n_children(iter) == 0) { + row_has_child_toggled(parent, iter); + } + } + }); + + model.rows_reordered.connect((subpath, subiter, new_order) => { + Gtk.TreePath path = child_path_to_path(name, subpath); + Gtk.TreeIter iter; + get_iter(out iter, path); + rows_reordered(path, iter, new_order); + }); + + groupModels[new String(name)] = model; + + index = 0; + foreach(String group in groupModels.keys) { + if(group.data == name) + break; + + index++; + } + + Gtk.TreePath path = new Gtk.TreePath.from_indices(index, -1); + Gtk.TreeIter iter; + get_iter(out iter, path); + row_inserted(path, iter); + + model.foreach((childModel, childPath, childIter) => { + Gtk.TreePath subpath = child_path_to_path(name, childPath); + Gtk.TreeIter subiter; + get_iter(out subiter, subpath); + row_inserted(subpath, subiter); + + return false; + }); + } + + public void remove_group(string name) { + int index = 0; + foreach(String group in groupModels.keys) { + if(group.data == name) { + Gee.LinkedList<Gtk.TreePath> children = new Gee.LinkedList<Gtk.TreePath>(); + + groupModels[group].foreach((childModel, childPath, childIter) => { + children.offer_head(childPath); + + return false; + }); + + while(!children.is_empty) { + row_deleted(child_path_to_path(name, children.poll_head())); + } + + Gtk.TreePath path = new Gtk.TreePath.from_indices(index, -1); + row_deleted(path); + + groupModels.unset(group); + + return; + } + + index++; + } + } + + private Gtk.TreePath child_path_to_path(string groupName, Gtk.TreePath childPath) { + int index = 0; + foreach(String group in groupModels.keys) { + if(group.data == groupName) { + Gtk.TreePath path = childPath.copy(); + path.prepend_index(index); + + return path; + } + + index++; + } + + assert_not_reached(); + } + + private int pointer_to_int(void* ptr) { + return (int)(long)ptr; + } + + private int child_iter_index(Gtk.TreeIter iter) { + for(int index = 0; index < childIters.size; ++index) { + if(childIters[index].stamp != iter.stamp) + continue; + if(childIters[index].user_data != iter.user_data) + continue; + if(childIters[index].user_data2 != iter.user_data2) + continue; + if(childIters[index].user_data3 != iter.user_data3) + continue; + + return index; + } + + childIters.add(iter); + return childIters.size-1; + } + + private Gtk.TreeIter get_nth_child_iter(int n) { + return childIters[n]; + } + + private Gtk.TreeIter? get_child_iter(Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + if(pointer_to_int(iter.user_data3) != -1) + return get_nth_child_iter(pointer_to_int(iter.user_data3)); + else + return null; + } + + private Gtk.TreeModel get_child_model(Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + return groupModels[iter.user_data2 as String]; + } + + public Type get_column_type(int index) { + switch(index) { + case 0: + return typeof(Object); + } + + assert_not_reached(); + } + + public Gtk.TreeModelFlags get_flags() { + return 0; + } + + public bool get_iter(out Gtk.TreeIter iter, Gtk.TreePath path) { + if(path.get_depth() == 0) + return false; + + unowned int[] indices = path.get_indices(); + int index = indices[0]; + if(index < 0 || index >= groupModels.size) + return false; + + Gee.MapIterator<String, Gtk.TreeModel> it = groupModels.map_iterator(); + it.first(); + for(int i = 0; i < index; ++i) + it.next(); + + iter.stamp = stamp; + iter.user_data = this; + iter.user_data2 = it.get_key(); + iter.user_data3 = (-1).to_pointer(); + + if(path.get_depth() > 1) { + Gtk.TreePath subpath = new Gtk.TreePath(); + for(int i = 1; i < path.get_depth(); ++i) { + subpath.append_index(indices[i]); + } + + Gtk.TreeIter subiter; + + if(!it.get_value().get_iter(out subiter, subpath)) + return false; + + iter.user_data3 = child_iter_index(subiter).to_pointer(); + } + + return true; + } + + public int get_n_columns() { + return 1; + } + + public Gtk.TreePath get_path(Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + + int index = 0; + + foreach(String group in groupModels.keys) { + if(group == iter.user_data2) + break; + + ++index; + } + + Gtk.TreePath subpath = new Gtk.TreePath(); + + if(pointer_to_int(iter.user_data3) != -1) { + Gtk.TreeIter subiter = get_child_iter(iter); + subpath = get_child_model(iter).get_path(subiter); + } + subpath.prepend_index(index); + return subpath; + } + + public void get_value(Gtk.TreeIter iter, int column, out Value value) { + assert (column == 0 && iter.stamp == stamp && iter.user_data == this); + + if(pointer_to_int(iter.user_data3) == -1) { + value = Value(typeof(String)); + value.take_object(iter.user_data2 as String); + } + else { + get_child_model(iter).get_value(get_child_iter(iter), 0, out value); + } + } + + public bool iter_children(out Gtk.TreeIter iter, Gtk.TreeIter? parent) { + if (parent == null) { + return get_iter(out iter, new Gtk.TreePath.from_indices(0, -1)); + } + + assert(parent.stamp == stamp && parent.user_data == this); + + Gtk.TreeIter childIter; + if(!get_child_model(parent).iter_children(out childIter, get_child_iter(parent))) + return false; + + iter.stamp = stamp; + iter.user_data = this; + iter.user_data2 = parent.user_data2; + iter.user_data3 = child_iter_index(childIter).to_pointer(); + + return true; + } + + public bool iter_has_child(Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + + return (iter_n_children(iter) > 0); + } + + public int iter_n_children(Gtk.TreeIter? iter) { + if (iter == null) + return groupModels.size; + + assert(iter.stamp == stamp && iter.user_data == this); + + return get_child_model(iter).iter_n_children(get_child_iter(iter)); + } + + public bool iter_next(ref Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + + if(pointer_to_int(iter.user_data3) == -1) { + bool next = false; + foreach(String group in groupModels.keys) { + if(next) { + iter.user_data2 = group; + return true; + } + + if(group == iter.user_data2) + next = true; + } + + iter.stamp = -1; + return false; + } + + else { + Gtk.TreeModel m = get_child_model(iter); + Gtk.TreeIter subiter = get_child_iter(iter); + if(!m.iter_next(ref subiter)) { + iter.stamp = -1; + return false; + } + + iter.user_data3 = child_iter_index(subiter).to_pointer(); + return true; + } + } + + public bool iter_nth_child(out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n) { + if (parent == null) { + return get_iter(out iter, new Gtk.TreePath.from_indices(n, -1)); + } + else { + Gtk.TreePath path = get_path(parent); + path.append_index(n); + return get_iter(out iter, path); + } + } + + public bool iter_parent(out Gtk.TreeIter iter, Gtk.TreeIter child) { + assert(child.stamp == stamp && child.user_data == this); + + if(pointer_to_int(child.user_data3) == -1) { + iter.stamp = -1; + return false; + } + + iter.stamp = stamp; + iter.user_data = this; + iter.user_data2 = child.user_data2; + + Gtk.TreeIter childParent; + if(get_child_model(child).iter_parent(out childParent, get_child_iter(child))) { + iter.user_data3 = child_iter_index(childParent).to_pointer(); + } + else { + iter.user_data3 = (-1).to_pointer(); + } + + return true; + } + + public void ref_node(Gtk.TreeIter iter) {} + public void unref_node(Gtk.TreeIter iter) {} + } + + private class ContactListModel : Object, Gtk.TreeModel { + private unowned ContactList contactList; + private int stamp = 0; + private Gee.TreeMap<string, Contact> entries = new Gee.TreeMap<string, Contact>(); + + + public ContactListModel(ContactList contactList0) { + contactList = contactList0; + } + + public void update_contact(Contact c) { + ++stamp; + bool added = true; + if(c.jid in entries.keys) { + added = false; + + foreach(string group in entries[c.jid].get_groups()) { + contactList.groups[group].unset(c.jid); + } + } + + entries[c.jid] = c; + + Gtk.TreeIter iter = Gtk.TreeIter(); + iter.stamp = stamp; + iter.user_data = this; + iter.user_data2 = c; + iter.user_data3 = null; + + Gtk.TreePath path = get_path(iter); + + if(added) + row_inserted(path, iter); + else + row_changed(path, iter); + + foreach(string group in c.get_groups()) { + if(!(group in contactList.groups.keys)) { + contactList.groups[group] = new Gee.HashMap<string, Contact>(); + } + + contactList.groups[group][c.jid] = c; + } + + contactList.update_groups(); + } + + public Contact get_contact(string jid) { + string bareJID = jid.split("/", 2)[0]; + + return entries[bareJID]; + } + + public Type get_column_type(int index) { + switch(index) { + case 0: + return typeof(Contact); + } + + assert_not_reached(); + } + + public Gtk.TreeModelFlags get_flags() { + return Gtk.TreeModelFlags.LIST_ONLY; + } + + public bool get_iter(out Gtk.TreeIter iter, Gtk.TreePath path) { + if(path.get_depth() != 1) + return false; + + int index = path.get_indices()[0]; + if(index < 0 || index >= entries.size) + return false; + + Gee.MapIterator<string, Contact> it = entries.map_iterator(); + it.first(); + for(int i = 0; i < index; ++i) + it.next(); + + iter.stamp = stamp; + iter.user_data = this; + iter.user_data2 = it.get_value(); + iter.user_data3 = null; + + return true; + } + + public int get_n_columns() { + return 1; + } + + public Gtk.TreePath get_path(Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + + int index = 0; + + foreach(Contact c in entries.values) { + if(c == iter.user_data2) + break; + + ++index; + } + + return new Gtk.TreePath.from_indices(index, -1); + } + + public void get_value(Gtk.TreeIter iter, int column, out Value value) { + assert (column == 0 && iter.stamp == stamp && iter.user_data == this); + + Contact c = iter.user_data2 as Contact; + value = Value(typeof(Contact)); + value.take_object(c); + } + + public bool iter_children(out Gtk.TreeIter iter, Gtk.TreeIter? parent) { + if (parent != null) { + iter.stamp = -1; + return false; + } + + return get_iter(out iter, new Gtk.TreePath.from_indices(0, -1)); + } + + public bool iter_has_child(Gtk.TreeIter iter) { + return false; + } + + public int iter_n_children(Gtk.TreeIter? iter) { + if (iter == null) + return entries.size; + else + return 0; + } + + public bool iter_next(ref Gtk.TreeIter iter) { + assert(iter.stamp == stamp && iter.user_data == this); + + bool next = false; + foreach(Contact c in entries.values) { + if(next) { + iter.user_data2 = c; + return true; + } + + if(c == iter.user_data2) + next = true; + } + + iter.stamp = -1; + return false; + } + + public bool iter_nth_child(out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n) { + if (parent != null) { + iter.stamp = -1; + return false; + } + + return get_iter(out iter, new Gtk.TreePath.from_indices(n, -1)); + } + + public bool iter_parent(out Gtk.TreeIter iter, Gtk.TreeIter child) { + iter.stamp = -1; + return false; + } + + public void ref_node(Gtk.TreeIter iter) {} + public void unref_node(Gtk.TreeIter iter) {} + } + + private class GroupFilter : Gtk.TreeModelFilter { + private string group; + + public GroupFilter(Gtk.TreeModel childModel, string group0) { + Object(child_model: childModel); + + group = group0; + + set_visible_func((model, iter) => { + Value value; + model.get_value(iter, 0, out value); + + Contact c = value.get_object() as Contact; + + if(c == null) + return true; + + return (group in c.get_groups()); + }); + } + } + + private class ContactListFilter : Gtk.TreeModelFilter { + private unowned ContactList contactList; + + private bool show_contact(Contact c) { + return (c.get_resource_with_highest_priority() != null); + } + + public ContactListFilter(ContactList contactList0, Gtk.TreeModel childModel) { + Object(child_model: childModel); + contactList = contactList0; + + set_visible_func((model, iter) => { + Value value; + model.get_value(iter, 0, out value); + + Contact c = value.get_object() as Contact; + + if(c != null) { + return show_contact(c); + } + else { + assert(value.get_object() is String); + string group = (value.get_object() as String).data; + + foreach(Gee.Map.Entry<string, Contact> contact in contactList.get_group(group)) { + if(show_contact(contact.value)) + return true; + } + + return false; + } + }); + } + } + + private class ContactListSorter : Gtk.TreeModelSort { + public ContactListSorter(Gtk.TreeModel childModel) { + Object(model: childModel); + + set_sort_column_id(0, Gtk.SortType.ASCENDING); + set_sort_func(0, (model, itera, iterb) => { + Value value; + model.get_value(itera, 0, out value); + Object a = value.get_object(); + model.get_value(iterb, 0, out value); + Object b = value.get_object(); + + if(a is String && b is String) { + return (a as String).data.collate((b as String).data); + } + else if(a is Contact && b is Contact) { + return (a as Contact).display_string.collate((b as Contact).display_string); + } + + assert_not_reached(); + }); + } + } +} |