summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2010-06-21 23:12:44 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2010-06-21 23:12:44 +0200
commitb9145a8161071bee25c6c1c026fbc2381bdfa0bb (patch)
treec4bedf170c6d6cb110c37086b9d864ac8e80522e
parent0671f09eb9c44aae49bb438a19a1de3abd1c0202 (diff)
downloadephraim-b9145a8161071bee25c6c1c026fbc2381bdfa0bb.tar
ephraim-b9145a8161071bee25c6c1c026fbc2381bdfa0bb.zip
Show roster
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/ephraim.erl45
-rw-r--r--src/core/ephraim_conn.erl25
-rw-r--r--src/core/ephraim_conv.erl6
-rw-r--r--src/core/ephraim_roster.erl109
-rw-r--r--src/core/ephraim_util.erl10
-rw-r--r--src/gui/CMakeLists.txt4
-rw-r--r--src/gui/CellRendererContact.vala13
-rw-r--r--src/gui/Contact.vala24
-rw-r--r--src/gui/CoreConnector.vala53
-rw-r--r--src/gui/Ephraim.vala14
-rw-r--r--src/gui/Roster.vala166
-rw-r--r--src/gui/ephraim.glade86
15 files changed, 444 insertions, 120 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 49f4f09..3ea896e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ ensure_vala_version("0.8" MINIMUM)
find_package(PkgConfig)
pkg_check_modules(GTK REQUIRED gtk+-2.0)
+pkg_check_modules(GEE REQUIRED gee-1.0)
pkg_check_modules(ERL REQUIRED erl_interface)
add_subdirectory(src)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 859b25d..939f542 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,6 @@
-add_definitions(${GTK_CFLAGS} ${GTK_CFLAGS_OTHER} ${ERL_CFLAGS} ${ERL_CFLAGS_OTHER})
-link_libraries(${GTK_LIBRARIES} ${ERL_LIBRARIES})
-link_directories(${GTK_LIBRARY_DIRS} ${ERL_LIBRARY_DIRS})
+add_definitions(${GTK_CFLAGS} ${GTK_CFLAGS_OTHER} ${GEE_CFLAGS} ${GEE_CFLAGS_OTHER} ${ERL_CFLAGS} ${ERL_CFLAGS_OTHER})
+link_libraries(${GTK_LIBRARIES} ${ERL_LIBRARIES} ${GEE_LIBRARIES})
+link_directories(${GTK_LIBRARY_DIRS} ${ERL_LIBRARY_DIRS} ${GEE_LIBRARY_DIRS})
add_subdirectory(core ${ephraim_BINARY_DIR}/core)
add_subdirectory(gui ${ephraim_BINARY_DIR}/gui)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 470e686..b0ab82c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -2,4 +2,6 @@ erl_target(ephraim-core
ephraim.erl
ephraim_conn.erl
ephraim_conv.erl
+ ephraim_roster.erl
+ ephraim_util.erl
)
diff --git a/src/core/ephraim.erl b/src/core/ephraim.erl
index 7be93d2..dc8382d 100644
--- a/src/core/ephraim.erl
+++ b/src/core/ephraim.erl
@@ -2,19 +2,15 @@
-compile([debug_info, export_all]).
-record(state, {
- conn :: pid(),
- convs :: dict(),
- uis :: set()
+ conn :: pid(),
+ roster :: pid(),
+ convs :: dict(),
+ uis :: set()
}).
-spec start() -> ok.
start() ->
- Pid = spawn(?MODULE, init, []),
- case register(ephraim, Pid) of
- true ->
- ok
- end,
- open_port({spawn_executable, "ephraim-gtk"}, [stream, use_stdio, out]).
+ spawn(?MODULE, init, []).
-spec stop() -> ok.
stop() ->
@@ -23,8 +19,12 @@ stop() ->
-spec init() -> ok.
init() ->
+ register(ephraim, self()),
+ open_port({spawn_executable, "ephraim-gtk"}, [stream, use_stdio, out]),
+
Conn = spawn(ephraim_conn, init, []),
- loop(#state{conn=Conn,convs=dict:new(),uis=sets:new()}),
+ Roster = spawn(ephraim_roster, init, []),
+ loop(#state{conn=Conn,roster=Roster,convs=dict:new(),uis=sets:new()}),
init:stop().
-spec get_conv(#state{},exmpp_jid:jid()) -> {#state{},pid()}.
@@ -43,14 +43,18 @@ get_conv(State, JID) ->
loop(State) ->
receive
stop ->
- dict:fold(fun(_,Conv,Msg) -> Conv ! Msg end, stop, State#state.convs),
+ %dict:fold(fun(_,Conv,Msg) -> Conv ! Msg end, stop, State#state.convs),
+ ephraim_util:send_all_values(State#state.convs, stop),
State#state.conn ! stop,
+ State#state.roster ! stop,
ok;
+
{register_ui, Pid} ->
io:format("Registered UI ~p~n", [Pid]),
UIs = State#state.uis,
State2 = State#state{uis=sets:add_element(Pid,UIs)},
loop(State2);
+
{unregister_ui, Pid} ->
io:format("Unregistered UI ~p~n", [Pid]),
UIs = State#state.uis,
@@ -62,10 +66,29 @@ loop(State) ->
ok
end,
loop(State2);
+
+ {ui_update, Msg} ->
+ ephraim_util:send_all(sets:to_list(State#state.uis), Msg),
+ loop(State);
+
+ {roster, Packet} ->
+ State#state.roster ! Packet,
+ loop(State);
+
{receive_message, From, Packet} ->
{State2, Conv} = get_conv(State, From),
Conv ! {receive_message, Packet},
loop(State2);
+
+ {receive_iq, IQ} ->
+ case IQ of
+ {iq, response, result, _, 'jabber:iq:roster', Payload, _, _, 'jabber:client'} ->
+ State#state.roster ! {roster_iq, Payload};
+ _ ->
+ io:format("ephraim: IQ: ~p~n", [IQ])
+ end,
+ loop(State);
+
Msg ->
io:format("ephraim: ~p~n", [Msg]),
loop(State)
diff --git a/src/core/ephraim_conn.erl b/src/core/ephraim_conn.erl
index 18edd6a..c4b163e 100644
--- a/src/core/ephraim_conn.erl
+++ b/src/core/ephraim_conn.erl
@@ -1,10 +1,9 @@
-module(ephraim_conn).
--include_lib("exmpp/include/exmpp_client.hrl").
-compile([debug_info, export_all]).
+-include_lib("exmpp/include/exmpp_client.hrl").
-record(conn_state, {
- session :: any(),
- roster :: any()
+ session :: any()
}).
-spec init() -> ok.
@@ -13,13 +12,20 @@ init() ->
Session = exmpp_session:start(),
JID = exmpp_jid:make("ephraim-test", "jabber.ccc.de", "Bar"),
exmpp_session:auth_basic_digest(Session, JID, "foobarbla"),
+ io:format("Connecting...~n"),
exmpp_session:connect_TCP(Session, "jabber.ccc.de", 5222),
+ io:format("Connected.~n"),
session(#conn_state{session=Session}).
-spec session(#conn_state{}) -> ok.
-session(State) ->
+session(State) ->
+ io:format("Logging in...~n"),
exmpp_session:login(State#conn_state.session),
+ io:format("Setting presence...~n"),
exmpp_session:send_packet(State#conn_state.session, exmpp_presence:set_status(exmpp_presence:available(), "Foo/Test\\Bar")),
+ io:format("Getting roster...~n"),
+ exmpp_session:send_packet(State#conn_state.session, exmpp_client_roster:get_roster("foo")),
+ io:format("Ok.~n"),
loop(State).
-spec loop(#conn_state{}) -> ok.
@@ -29,16 +35,19 @@ loop(State) ->
exmpp_session:stop(State#conn_state.session);
#received_packet{packet_type=presence, raw_packet=Packet} ->
- From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>),
- Type = atom_to_list(exmpp_presence:get_type(Packet)),
- Status = exmpp_presence:get_status(Packet),
- io:format("~ts is now ~ts: ~ts~n", [From, Type, Status]),
+ ephraim ! {roster, {presence, Packet}},
loop(State);
#received_packet{packet_type=message, raw_packet=Packet} ->
From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>),
ephraim ! {receive_message, From, Packet},
loop(State);
+
+ #received_packet{packet_type=iq, raw_packet=Packet} ->
+ IQ = exmpp_iq:xmlel_to_iq(Packet),
+ ephraim ! {receive_iq, IQ},
+ loop(State);
+
Msg ->
io:format("ephraim_conn: ~p~n", [Msg]),
loop(State)
diff --git a/src/core/ephraim_conv.erl b/src/core/ephraim_conv.erl
index 00b2bc1..8f53098 100644
--- a/src/core/ephraim_conv.erl
+++ b/src/core/ephraim_conv.erl
@@ -16,9 +16,11 @@ loop(State) ->
receive
stop ->
ok;
- {receive_message, Packet} ->
- io:format("Received packet from ~p: ~p~n", [State#conv_state.jid, Packet]),
+
+ {receive_message, _Packet} ->
+ %io:format("Received packet from ~p: ~p~n", [State#conv_state.jid, Packet]),
loop(State);
+
Msg ->
io:format("ephraim_conv (~p): ~p~n", [State#conv_state.jid, Msg]),
loop(State)
diff --git a/src/core/ephraim_roster.erl b/src/core/ephraim_roster.erl
new file mode 100644
index 0000000..4f8dfef
--- /dev/null
+++ b/src/core/ephraim_roster.erl
@@ -0,0 +1,109 @@
+-module(ephraim_roster).
+-compile([debug_info, export_all]).
+-include_lib("exmpp/include/exmpp.hrl").
+
+-record(roster, {
+ entries :: dict()
+ }).
+
+-record(roster_entry, {
+ name :: binary() | undefined,
+ subscription :: atom() | undefined,
+ resources :: dict()
+ }).
+
+-record(resource_entry, {
+ priority :: integer(),
+ show :: atom(),
+ status :: string()
+ }).
+
+
+-spec init() -> ok.
+init() ->
+ loop(#roster{entries=dict:new()}).
+
+-spec updateResource(#roster{}, binary(), integer(), atom(), atom(), string()) -> #roster{}.
+updateResource(Roster, JID, Priority, Type, Show, Status) ->
+ {Node, Domain, Resource} = exmpp_jid:to_lower(exmpp_jid:parse(JID)),
+ BareJID = list_to_binary([Node, <<"@">>, Domain]),
+
+ RosterEntry = case dict:find(BareJID, Roster#roster.entries) of
+ {ok, Entry} ->
+ Entry;
+ error ->
+ #roster_entry{resources=dict:new()}
+ end,
+
+ Resources = case Type of
+ available ->
+ ResourceEntry = #resource_entry{priority=Priority,show=Show,status=Status},
+ dict:store(Resource, ResourceEntry, RosterEntry#roster_entry.resources);
+ _ ->
+ RosterEntry#roster_entry.resources
+ end,
+ Entries = dict:store(BareJID, RosterEntry#roster_entry{resources=Resources}, Roster#roster.entries),
+ if RosterEntry#roster_entry.subscription =:= undefined ->
+ ok;
+ true ->
+ ephraim ! {ui_update, {roster_update, JID, dict:to_list(Resources)}}
+ end,
+ Roster#roster{entries=Entries}.
+
+-spec updateRosterEntry(#roster{}, binary(), binary(), atom()) -> #roster{}.
+updateRosterEntry(Roster, JID, Name, Subscription) ->
+ Resources = case dict:find(JID, Roster#roster.entries) of
+ {ok, Entry} ->
+ Entry#roster_entry.resources;
+ error ->
+ dict:new()
+ end,
+ NewEntry = #roster_entry{subscription=Subscription,name=Name,resources=Resources},
+ Entries = dict:store(JID, NewEntry, Roster#roster.entries),
+ ephraim ! {ui_update, {roster_update, JID, dict:to_list(Resources)}},
+ Roster#roster{entries=Entries}.
+
+-spec handleRosterIQ(#roster{}, #xmlel{}) -> #roster{}.
+handleRosterIQ(Roster, Item) ->
+ JID = exmpp_xml:get_attribute(Item, jid, undefined),
+ Name = exmpp_xml:get_attribute(Item, name, undefined),
+ Subscription = binary_to_atom(exmpp_xml:get_attribute(Item, subscription, undefined), utf8),
+
+ updateRosterEntry(Roster, JID, Name, Subscription).
+
+-spec handleRosterIQs(#roster{}, [#xmlel{}]) -> #roster{}.
+handleRosterIQs(Roster, []) ->
+ Roster;
+
+handleRosterIQs(Roster, [Item = #xmlel{name=item}|Rest]) ->
+ Roster2 = handleRosterIQ(Roster, Item),
+ handleRosterIQs(Roster2, Rest);
+
+handleRosterIQs(Roster, [_|Rest]) ->
+ handleRosterIQs(Roster, Rest).
+
+-spec loop(#roster{}) -> ok.
+loop(Roster) ->
+ receive
+ stop ->
+ ok;
+
+ {presence, Packet} ->
+ From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>),
+
+ Priority = exmpp_presence:get_priority(Packet),
+ Type = exmpp_presence:get_type(Packet),
+ Show = exmpp_presence:get_show(Packet),
+ Status = exmpp_presence:get_status(Packet),
+
+ Roster2 = updateResource(Roster, From, Priority, Type, Show, Status),
+ loop(Roster2);
+
+ {roster_iq, Payload} ->
+ Roster2 = handleRosterIQs(Roster, Payload#xmlel.children),
+ loop(Roster2);
+
+ Msg ->
+ io:format("ephraim_roster: ~p~n", [Msg]),
+ loop(Roster)
+ end.
diff --git a/src/core/ephraim_util.erl b/src/core/ephraim_util.erl
new file mode 100644
index 0000000..73cb7fc
--- /dev/null
+++ b/src/core/ephraim_util.erl
@@ -0,0 +1,10 @@
+-module(ephraim_util).
+-compile([debug_info, export_all]).
+
+-spec send_all([term()], term()) -> term().
+send_all(Dest, Message) ->
+ lists:foldr(fun erlang:send/2, Message, Dest).
+
+-spec send_all_values(dict(), term()) -> term().
+send_all_values(Dest, Message) ->
+ dict:fold(fun(_,Value,Msg) -> Value ! Msg end, Message, Dest).
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 8839e16..d2a464c 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -2,9 +2,13 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ephraim_BINARY_DIR})
vala_precompile(VALA_C
"Ephraim.vala"
+ "CellRendererContact.vala"
+ "Contact.vala"
"CoreConnector.vala"
+ "Roster.vala"
PACKAGES
gtk+-2.0
+ gee-1.0
erl_interface
OPTIONS
--thread
diff --git a/src/gui/CellRendererContact.vala b/src/gui/CellRendererContact.vala
new file mode 100644
index 0000000..f8aa6a1
--- /dev/null
+++ b/src/gui/CellRendererContact.vala
@@ -0,0 +1,13 @@
+public class CellRendererContact : Gtk.CellRendererText {
+ private Contact _contact;
+
+ public Contact contact {
+ get {
+ return _contact;
+ }
+ set {
+ _contact = value;
+ text = contact.jid;
+ }
+ }
+}
diff --git a/src/gui/Contact.vala b/src/gui/Contact.vala
new file mode 100644
index 0000000..2eb40ae
--- /dev/null
+++ b/src/gui/Contact.vala
@@ -0,0 +1,24 @@
+public class Contact : Object {
+ public enum Show {
+ ONLINE, AWAY, CHAT, DND, XA, UNDEFINED
+ }
+
+ public enum Subscription {
+ BOTH
+ }
+
+ public Contact(string jid0) {
+ Object(jid: jid0);
+ }
+
+ public class Resource : Object {
+ public int priority {get; construct;}
+ public Show show {get; construct;}
+ public string status {get; construct;}
+ }
+
+ public string jid {get; construct;}
+ public Subscription subscription {get; set;}
+ public string name {get; set;}
+ public Gee.HashMap<string, Resource> resources {get; private set;}
+}
diff --git a/src/gui/CoreConnector.vala b/src/gui/CoreConnector.vala
index 9edbc7d..fdd74ee 100644
--- a/src/gui/CoreConnector.vala
+++ b/src/gui/CoreConnector.vala
@@ -2,6 +2,12 @@ public class CoreConnector {
unowned Thread thread;
bool running;
+ Erl.Node node;
+ Erl.Connection con;
+ Erl.Term self;
+
+ Roster roster;
+
private class TermStore {
public Erl.Term term;
}
@@ -10,8 +16,9 @@ public class CoreConnector {
Erl.init();
}
- public CoreConnector() {
+ public CoreConnector(Roster roster0) {
running = false;
+ roster = roster0;
}
public bool start() {
@@ -20,6 +27,13 @@ public class CoreConnector {
running = true;
+ node = Erl.Node("ephraim-gtk", "magiccookie", 0);
+ con = node.connect("ephraim-core@avalon.local");
+
+ self = Erl.mk_self_pid(node);
+
+ con.reg_send("ephraim", Erl.format("{register_ui,~w}", self));
+
try {
thread = Thread.create(receive, true);
return true;
@@ -34,16 +48,12 @@ public class CoreConnector {
running = false;
thread.join();
+
+ con.reg_send("ephraim", Erl.format("{unregister_ui,~w}", self));
}
+
private void* receive() {
- Erl.Node node = Erl.Node("ephraim-gtk", "magiccookie", 0);
- Erl.Connection con = node.connect("ephraim-core@avalon.local");
-
- Erl.Term self = Erl.mk_self_pid(node);
-
- con.reg_send("ephraim", Erl.format("{register_ui,~w}", self));
-
while(running) {
TermStore response = new TermStore();
Erl.ReceiveType ret = con.receive(out response.term, 1000);
@@ -64,14 +74,31 @@ public class CoreConnector {
}
}
- con.reg_send("ephraim", Erl.format("{unregister_ui,~w}", self));
-
return null;
}
+ private Erl.Term? match(string pattern, Erl.Term term) {
+ Erl.Term pattern_term = Erl.format(pattern);
+
+ if(pattern_term.match(term) != 0)
+ return pattern_term;
+ else
+ return null;
+ }
+
private void handleTerm(TermStore store) {
- unowned Erl.Term term = store.term;
- Erl.print_term(stdout, term);
- stdout.printf("\n");
+ unowned Erl.Term term = store.term;
+ Erl.Term match_term;
+
+ if((match_term = match("{roster_update,JID,Resources}", term)) != null) {
+ Erl.Term JID_term = match_term.var_content("JID");
+ string JID = ((string)JID_term.bin_ptr()).ndup(JID_term.bin_size());
+
+ roster.update_contact(new Contact(JID));
+ }
+ else {
+ Erl.print_term(stdout, term);
+ stdout.printf("\n");
+ }
}
}
diff --git a/src/gui/Ephraim.vala b/src/gui/Ephraim.vala
index a52c4df..cf87965 100644
--- a/src/gui/Ephraim.vala
+++ b/src/gui/Ephraim.vala
@@ -9,14 +9,24 @@ public class Ephraim {
return 1;
}
- CoreConnector coreconn = new CoreConnector();
+ Roster roster = new Roster();
+ CoreConnector coreconn = new CoreConnector(roster);
if(!coreconn.start())
return 1;
unowned Gtk.Window window = builder.get_object("MainWindow") as Gtk.Window;
window.hide.connect(Gtk.main_quit);
- window.show();
+
+ unowned Gtk.MenuItem quitItem = builder.get_object("MenuItemQuit") as Gtk.MenuItem;
+ quitItem.activate.connect(() => window.visible = false);
+
+ unowned Gtk.TreeView rosterView = builder.get_object("Roster") as Gtk.TreeView;
+ rosterView.set_model(roster.get_default_group());
+
+ rosterView.append_column(new Gtk.TreeViewColumn.with_attributes("Contact", new CellRendererContact(), "contact", 0, null));
+
+ window.visible = true;
Gtk.main();
diff --git a/src/gui/Roster.vala b/src/gui/Roster.vala
new file mode 100644
index 0000000..2ef4618
--- /dev/null
+++ b/src/gui/Roster.vala
@@ -0,0 +1,166 @@
+public class Roster : Object {
+ private int stamp;
+ private Gee.TreeMap<string, Contact> entries = new Gee.TreeMap<string, Contact>();
+ private Gee.TreeMap<string, RosterGroup> groups = new Gee.TreeMap<string, RosterGroup>();
+
+
+ public Roster() {
+ stamp = 0;
+ groups["default"] = new RosterGroup(this);
+ }
+
+ public Gtk.TreeModel get_default_group() {
+ return groups["default"];
+ }
+
+ public void update_contact(Contact c) {
+ entries[c.jid] = c;
+ groups["default"].update_contact(c);
+ }
+
+ private class RosterGroup : Object, Gtk.TreeModel {
+ private unowned Roster roster {get; set;}
+ private Gee.TreeMap<string, Contact> entries = new Gee.TreeMap<string, Contact>();
+
+
+ public RosterGroup(Roster roster0) {
+ roster = roster0;
+ }
+
+ public void update_contact(Contact c) {
+ bool added = !entries.has_key(c.jid);
+
+ entries[c.jid] = c;
+
+ Gtk.TreeIter iter = Gtk.TreeIter();
+ iter.stamp = roster.stamp;
+ iter.user_data = this;
+ iter.user_data2 = c;
+
+ Gtk.TreePath path = get_path(iter);
+
+ if (added)
+ row_inserted(path, iter);
+ else
+ row_changed(path, iter);
+ }
+
+ public Type get_column_type (int index) {
+ switch(index) {
+ case 0:
+ return typeof(Contact);
+ }
+
+ return Type.INVALID;
+ }
+
+ 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 = roster.stamp;
+ iter.user_data = this;
+ iter.user_data2 = it.get_value();
+
+ return true;
+ }
+
+ public int get_n_columns () {
+ return 1;
+ }
+
+ public Gtk.TreePath get_path (Gtk.TreeIter iter) {
+ if(iter.stamp != roster.stamp || iter.user_data != this)
+ return (Gtk.TreePath)null;
+
+ 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) {
+ if (column != 0 || iter.stamp != roster.stamp || iter.user_data != this)
+ return;
+
+ 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) {
+ if(iter.stamp != roster.stamp || iter.user_data != this)
+ return false;
+
+ 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) {}
+ }
+}
diff --git a/src/gui/ephraim.glade b/src/gui/ephraim.glade
index b1b3455..629e2de 100644
--- a/src/gui/ephraim.glade
+++ b/src/gui/ephraim.glade
@@ -13,50 +13,18 @@
<child>
<object class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Datei</property>
+ <property name="label" translatable="yes">_Aktionen</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu1">
<property name="visible">True</property>
<child>
- <object class="GtkImageMenuItem" id="imagemenuitem1">
- <property name="label">gtk-new</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem2">
- <property name="label">gtk-open</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem3">
- <property name="label">gtk-save</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem4">
- <property name="label">gtk-save-as</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
</object>
</child>
<child>
- <object class="GtkImageMenuItem" id="imagemenuitem5">
+ <object class="GtkImageMenuItem" id="MenuItemQuit">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
@@ -68,53 +36,9 @@
</object>
</child>
<child>
- <object class="GtkMenuItem" id="menuitem2">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Bearbeiten</property>
- <property name="use_underline">True</property>
- <child type="submenu">
- <object class="GtkMenu" id="menu2">
- <property name="visible">True</property>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem6">
- <property name="label">gtk-cut</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem7">
- <property name="label">gtk-copy</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem8">
- <property name="label">gtk-paste</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- <child>
- <object class="GtkImageMenuItem" id="imagemenuitem9">
- <property name="label">gtk-delete</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
<object class="GtkMenuItem" id="menuitem3">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Ansicht</property>
+ <property name="label" translatable="yes">A_nsicht</property>
<property name="use_underline">True</property>
</object>
</child>
@@ -148,11 +72,12 @@
<object class="GtkHPaned" id="RosterPane">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="position">160</property>
<child>
<object class="GtkTreeView" id="Roster">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="model">RosterStore</property>
+ <property name="headers_visible">False</property>
</object>
<packing>
<property name="resize">False</property>
@@ -196,5 +121,4 @@
</object>
</child>
</object>
- <object class="GtkListStore" id="RosterStore"/>
</interface>