summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/ephraim.erl2
-rw-r--r--src/core/ephraim_roster.erl3
-rw-r--r--src/gui/CellRendererContact.vala22
-rw-r--r--src/gui/Contact.vala14
-rw-r--r--src/gui/CoreConnector.vala33
-rw-r--r--src/gui/Ephraim.vala2
-rw-r--r--src/gui/Roster.vala125
7 files changed, 155 insertions, 46 deletions
diff --git a/src/core/ephraim.erl b/src/core/ephraim.erl
index c8783f0..c59be72 100644
--- a/src/core/ephraim.erl
+++ b/src/core/ephraim.erl
@@ -143,7 +143,7 @@ loop(State) ->
{iq, response, result, _, 'vcard-temp', Payload, _, _, 'jabber:client'} ->
State#state.roster ! {vcard_iq, From, Payload};
_ ->
- io:format("ephraim: IQ from ~p: ~p~n", [From, IQ])
+ ok %io:format("ephraim: IQ from ~p: ~p~n", [From, IQ])
end,
loop(State);
diff --git a/src/core/ephraim_roster.erl b/src/core/ephraim_roster.erl
index aa31fd1..362b788 100644
--- a/src/core/ephraim_roster.erl
+++ b/src/core/ephraim_roster.erl
@@ -125,7 +125,6 @@ handleRosterIQ(Roster, Item) ->
Subscription = binary_to_atom(exmpp_xml:get_attribute(Item, subscription, undefined), utf8),
ephraim ! {send_packet, exmpp_stanza:set_recipient(exmpp_iq:get('jabber:client', #xmlel{ns='vcard-temp',name='vCard'}), JID)},
- io:format("ephraim_roster: ~p~n", [exmpp_stanza:set_recipient(exmpp_iq:get('jabber:client', #xmlel{ns='vcard-temp',name='vCard'}), JID)]),
updateRosterEntry(Roster, JID, Name, Subscription, Groups).
@@ -215,6 +214,6 @@ loop(Roster) ->
loop(Roster);
Msg ->
- io:format("ephraim_roster: ~p~n", [Msg]),
+ %io:format("ephraim_roster: ~p~n", [Msg]),
loop(Roster)
end.
diff --git a/src/gui/CellRendererContact.vala b/src/gui/CellRendererContact.vala
index 924b601..26497ec 100644
--- a/src/gui/CellRendererContact.vala
+++ b/src/gui/CellRendererContact.vala
@@ -38,14 +38,23 @@ public class CellRendererContact : Gtk.CellRenderer {
Gee.Map.Entry<string, Contact.Resource> res = contact.get_resource_with_highest_priority();
if(res != null && res.value.status != null) {
- str += "\n<span size=\"small\" fgcolor=\"grey40\" style=\"italic\">" + Markup.escape_text(res.value.status) + "</span>";
+ string status = res.value.status;
+ if(status.chr(-1, '\n') != null) {
+ status = status.split("\n", 2)[0] + "...";
+ }
+ str += "\n<span size=\"small\" fgcolor=\"grey40\" style=\"italic\">" + Markup.escape_text(status) + "</span>";
}
textRenderer.markup = str;
Gee.Map.Entry<string, Contact.Resource>? r = contact.get_resource_with_highest_priority();
if(r == null) {
- presenceRenderer.pixbuf = offline;
+ if(contact.subscription.is_to()) {
+ presenceRenderer.pixbuf = offline;
+ }
+ else {
+ presenceRenderer.pixbuf = undefined;
+ }
}
else {
switch(r.value.show) {
@@ -68,9 +77,13 @@ public class CellRendererContact : Gtk.CellRenderer {
}
avatarRenderer.pixbuf = contact.avatar;
+
+ cell_background = "#fff";
}
else if(_data is String) {
- textRenderer.text = (_data as String).data;
+ textRenderer.markup = "<i>" + Markup.escape_text((_data as String).data) + "</i>";
+
+ cell_background = "#ddd";
}
}
}
@@ -96,8 +109,6 @@ public class CellRendererContact : Gtk.CellRenderer {
height = calc_height;
if(cell_area != null) {
- if(&width != null)
- width = cell_area.width;
if(&x_offset != null)
x_offset = 0;
if(&y_offset != null)
@@ -162,5 +173,6 @@ public class CellRendererContact : Gtk.CellRenderer {
Object();
textRenderer.ellipsize = Pango.EllipsizeMode.END;
+ cell_background_set = true;
}
}
diff --git a/src/gui/Contact.vala b/src/gui/Contact.vala
index 4ced825..512c33f 100644
--- a/src/gui/Contact.vala
+++ b/src/gui/Contact.vala
@@ -20,6 +20,8 @@ public class Contact : Object {
public Contact(string jid0, string? name0) {
Object(jid: jid0, name: name0);
+ subscription = Subscription.NONE;
+
update_display_string();
}
@@ -40,7 +42,7 @@ public class Contact : Object {
if (name != null)
display_string = name;
else
- display_string = jid;
+ display_string = jid.split("@", 2)[0];
}
public Gee.Map.Entry<string, Resource>? get_resource_with_highest_priority() {
@@ -68,7 +70,15 @@ public class Contact : Object {
}
public enum Subscription {
- BOTH
+ NONE, PENDING_OUT, PENDING_IN, PENDING_BOTH, TO, TO_PENDING_IN, FROM, FROM_PENDING_OUT, BOTH;
+
+ public bool is_to() {
+ return (this == TO || this == TO_PENDING_IN || this == BOTH);
+ }
+
+ public bool is_from() {
+ return (this == FROM || this == FROM_PENDING_OUT || this == BOTH);
+ }
}
public class Resource : Object {
diff --git a/src/gui/CoreConnector.vala b/src/gui/CoreConnector.vala
index 5566686..d5f0ecc 100644
--- a/src/gui/CoreConnector.vala
+++ b/src/gui/CoreConnector.vala
@@ -32,6 +32,9 @@ public class CoreConnector {
}
private static string from_utf8(Eva.Binary bin) {
+ if(bin.len == 0)
+ return "";
+
string ret = ((string)bin.data).ndup(bin.len);
warn_if_fail(ret.validate());
return ret;
@@ -69,13 +72,34 @@ public class CoreConnector {
Eva.Binary name_term = match["Name"] as Eva.Binary;
string? name;
- if (name_term is Eva.Binary)
+ if (name_term != null)
name = from_utf8(name_term);
else
name = null;
Contact contact = new Contact(jid, name);
-
+
+ Eva.Atom subscription = match["Subscription"] as Eva.Atom;
+ if(subscription != null) {
+ switch(subscription.value) {
+ case "none":
+ contact.subscription = Contact.Subscription.NONE;
+ break;
+ case "to":
+ contact.subscription = Contact.Subscription.TO;
+ break;
+ case "from":
+ contact.subscription = Contact.Subscription.FROM;
+ break;
+ case "both":
+ contact.subscription = Contact.Subscription.BOTH;
+ break;
+ default:
+ stderr.printf("Unknown subscription: %s\r\n", subscription.value);
+ break;
+ }
+ }
+
Eva.Cons groups = match["Groups"] as Eva.Cons;
while(groups != null) {
Eva.Binary group_term = groups.head as Eva.Binary;
@@ -122,8 +146,11 @@ public class CoreConnector {
}
string? status = null;
- if(status_term != null)
+ if(status_term != null) {
status = from_utf8(status_term);
+ if(status == "")
+ status = null;
+ }
contact.update_resource(rname, new Contact.Resource(prio, show, status));
}
diff --git a/src/gui/Ephraim.vala b/src/gui/Ephraim.vala
index f13516e..b0e2571 100644
--- a/src/gui/Ephraim.vala
+++ b/src/gui/Ephraim.vala
@@ -42,7 +42,7 @@ public class Ephraim {
});
coreconn.chat_message.connect((jid, type, from, message) => {
- if(!(jid in conversations))
+ if(!(jid in conversations.keys))
return;
conversations[jid].chat_message(type, from, message);
diff --git a/src/gui/Roster.vala b/src/gui/Roster.vala
index ca1ef16..32bdb6d 100644
--- a/src/gui/Roster.vala
+++ b/src/gui/Roster.vala
@@ -2,17 +2,16 @@ public class Roster {
private Gtk.TreeView rosterView;
private ContactListModel contactList;
private RosterModel model;
- private Gee.Map<string, int> groups = new Gee.HashMap<string, int>();
+ 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 Roster(Gtk.TreeView rosterView0) {
rosterView = rosterView0;
contactList = new ContactListModel(this);
model = new RosterModel(contactList);
-
- rosterView.set_model(model);
+
+ rosterView.set_model(new RosterFilter(this, model));
rosterView.query_tooltip.connect((x, y, keyboard_tip, tooltip) => {
Gtk.TreeModel model;
@@ -57,7 +56,7 @@ public class Roster {
start_conversation(contact.jid + "/" + res.key);
});
- model.row_inserted.connect((path, iter) => {
+ rosterView.model.row_inserted.connect((path, iter) => {
rosterView.expand_to_path(path);
});
@@ -74,17 +73,21 @@ public class Roster {
}
private void update_groups() {
- foreach(Gee.Map.Entry<string, int> group in groups) {
- if(group.value <= 0) {
- model.removeGroup(group.key);
- groups.remove(group.key);
+ 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.addGroup(group.key);
+ model.add_group(group.key);
}
}
}
-
+
+ public Gee.Map<string, Contact> get_group(string group) {
+ return groups[group];
+ }
+
private class RosterModel : Object, Gtk.TreeModel {
private int stamp = 0;
private Gee.Map<String, Gtk.TreeModel> groupModels = new Gee.TreeMap<String, Gtk.TreeModel>();
@@ -101,10 +104,19 @@ public class Roster {
contactList = contactList0;
}
- public void addGroup(string name) {
+ public void add_group(string name) {
+ int index = 0;
foreach(String group in groupModels.keys) {
- if(group.data == name)
+ 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);
@@ -119,12 +131,20 @@ public class Roster {
model.row_deleted.connect((path) => {
path.prepend_index(0);
row_deleted(path);
- incrementStamp();
+
+ 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);
- incrementStamp();
Gtk.TreeIter iter;
get_iter(out iter, path);
row_has_child_toggled(path, iter);
@@ -132,15 +152,26 @@ public class Roster {
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);
- incrementStamp();
Gtk.TreeIter iter;
get_iter(out iter, path);
rows_reordered(path, iter, new_order);
@@ -148,7 +179,7 @@ public class Roster {
groupModels[new String(name)] = model;
- int index = 0;
+ index = 0;
foreach(String group in groupModels.keys) {
if(group.data == name)
break;
@@ -171,7 +202,7 @@ public class Roster {
});
}
- public void removeGroup(string name) {
+ public void remove_group(string name) {
int index = 0;
foreach(String group in groupModels.keys) {
if(group.data == name) {
@@ -190,7 +221,7 @@ public class Roster {
Gtk.TreePath path = new Gtk.TreePath.from_indices(index, -1);
row_deleted(path);
- groupModels.remove(group);
+ groupModels.unset(group);
return;
}
@@ -454,20 +485,14 @@ public class Roster {
public void update_contact(Contact c) {
++stamp;
bool added = true;
- if(c.jid in entries) {
+ if(c.jid in entries.keys) {
added = false;
foreach(string group in entries[c.jid].get_groups()) {
- if(group in roster.groups) {
- roster.groups[group] = roster.groups[group]-1;
- }
- else {
- warn_if_reached();
- }
+ roster.groups[group].unset(c.jid);
}
}
-
entries[c.jid] = c;
Gtk.TreeIter iter = Gtk.TreeIter();
@@ -484,10 +509,11 @@ public class Roster {
row_changed(path, iter);
foreach(string group in c.get_groups()) {
- if(!(group in roster.groups))
- roster.groups[group] = 0;
-
- roster.groups[group] = roster.groups[group]+1;
+ if(!(group in roster.groups.keys)) {
+ roster.groups[group] = new Gee.HashMap<string, Contact>();
+ }
+
+ roster.groups[group][c.jid] = c;
}
roster.update_groups();
@@ -632,9 +658,44 @@ public class Roster {
if(c == null)
return true;
-
+
return (group in c.get_groups());
});
}
}
+
+ private class RosterFilter : Gtk.TreeModelFilter {
+ private unowned Roster roster;
+
+ private bool show_contact(Contact c) {
+ return (c.get_resource_with_highest_priority() != null);
+ }
+
+ public RosterFilter(Roster roster0, Gtk.TreeModel childModel) {
+ Object(child_model: childModel);
+ roster = roster0;
+
+ 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 roster.get_group(group)) {
+ if(show_contact(contact.value))
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+ }
}