[Tkabber-dev] r1649 - in trunk/tkabber: . ifacetk plugins/roster

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Fri Feb 13 20:23:26 MSK 2009


Author: sergei
Date: 2009-02-13 20:23:25 +0300 (Fri, 13 Feb 2009)
New Revision: 1649

Modified:
   trunk/tkabber/ChangeLog
   trunk/tkabber/chats.tcl
   trunk/tkabber/ifacetk/iroster.tcl
   trunk/tkabber/muc.tcl
   trunk/tkabber/plugins/roster/conferences.tcl
   trunk/tkabber/utils.tcl
Log:
	* muc.tcl: Move focus and scroll down to 'add new JID' field in MUC
	  lists (thanks to Serge Yudin).

	* utils.tcl: Use \u0000 as a split character in msplit routine.

	* chats.tcl: Slightly optimized drawing room roster.

	* plugins/roster/conferences.tcl: Use storing/retrieving notes from
	  TclXMPP.

	* ifacetk/iroster.tcl: Added metacontacts support. Unfinished yet,
	  editing metacontacts is not possible, but grouping them in roster
	  works. Also, fixed bug which appears when a group name coinsides
	  with a JID in roster. Also, removed aliases support in favor of
	  metacontacts.


Modified: trunk/tkabber/ChangeLog
===================================================================
--- trunk/tkabber/ChangeLog	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/ChangeLog	2009-02-13 17:23:25 UTC (rev 1649)
@@ -1,3 +1,21 @@
+2009-02-13  Sergei Golovan  <sgolovan at nes.ru>
+
+	* muc.tcl: Move focus and scroll down to 'add new JID' field in MUC
+	  lists (thanks to Serge Yudin).
+
+	* utils.tcl: Use \u0000 as a split character in msplit routine.
+
+	* chats.tcl: Slightly optimized drawing room roster.
+
+	* plugins/roster/conferences.tcl: Use storing/retrieving notes from
+	  TclXMPP.
+
+	* ifacetk/iroster.tcl: Added metacontacts support. Unfinished yet,
+	  editing metacontacts is not possible, but grouping them in roster
+	  works. Also, fixed bug which appears when a group name coinsides
+	  with a JID in roster. Also, removed aliases support in favor of
+	  metacontacts.
+
 2009-02-11  Sergei Golovan  <sgolovan at nes.ru>
 
 	* private.tcl: Removed in favour of private XML storage interface in

Modified: trunk/tkabber/chats.tcl
===================================================================
--- trunk/tkabber/chats.tcl	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/chats.tcl	2009-02-13 17:23:25 UTC (rev 1649)
@@ -1039,7 +1039,6 @@
 		}
 		set userswin [users_win $chatid]
 		if {![lcontain $grouproster(users,$chatid) $jid]} {
-		    #roster::addline $userswin jid $nick $jid
 		    lappend grouproster(users,$chatid) $jid
 		    set grouproster(status,$chatid,$jid) $status
 
@@ -1050,8 +1049,6 @@
 		    muc::report_available $chatid $nick 0
 		}
 		set grouproster(status,$chatid,$jid) $status
-		ifacetk::roster::changeicon $userswin $jid roster/user/$status
-		ifacetk::roster::changeforeground $userswin $jid $status
 		chat::redraw_roster_after_idle $chatid
 	    }
 	}
@@ -1152,7 +1149,7 @@
     foreach level $levels {
 	ifacetk::roster::addline $userswin group \
 	    "[crange $level 1 end] ([llength $levelusers($level)])" \
-	    [crange $level 1 end] [crange $level 1 end] 0
+	    [crange $level 1 end] [crange $level 1 end] {} 0
 	set jid_nicks {}
 	foreach jid $levelusers($level) {
 	    lappend jid_nicks [list $jid [get_nick $xlib $jid groupchat]]
@@ -1163,18 +1160,14 @@
 	    lassign $item jid nick
 	    set status $grouproster(status,$group,$jid)
 
-	    ifacetk::roster::addline $userswin jid $nick \
-		[list $xlib $jid] [crange $level 1 end] 0
-	    ifacetk::roster::changeicon $userswin \
-		[list $xlib $jid] roster/user/$status
 	    if {$::plugins::nickcolors::options(use_colored_roster_nicks)} {
-		ifacetk::roster::changeforeground $userswin \
-		    [list $xlib $jid] $status \
-		    [plugins::nickcolors::get_color $nick]
+		set foreground [plugins::nickcolors::get_color $nick]
 	    } else {
-		ifacetk::roster::changeforeground $userswin \
-		    [list $xlib $jid] $status
+		set foreground [ifacetk::roster::get_foreground $status]
 	    }
+	    ifacetk::roster::addline $userswin jid $nick \
+		[list $xlib $jid] [crange $level 1 end] {} 0 \
+		{} roster/user/$status $foreground
 	}
     }
 

Modified: trunk/tkabber/ifacetk/iroster.tcl
===================================================================
--- trunk/tkabber/ifacetk/iroster.tcl	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/ifacetk/iroster.tcl	2009-02-13 17:23:25 UTC (rev 1649)
@@ -3,61 +3,61 @@
 Tree .faketree
 foreach {k v} [list background       White \
 		    foreground       Black] {
-    if {[cequal [set t$k [option get .faketree $k Tree]] ""]} {
+    if {[string equal [set t$k [option get .faketree $k Tree]] ""]} {
 	set t$k $v
     }
 }
 destroy .faketree
+
 Button .fakebutton
 foreach {k v} [list background       Gray \
 		    activeBackground LightGray] {
-    if {[cequal [set b$k [option get .fakebutton $k Button]] ""]} {
+    if {[string equal [set b$k [option get .fakebutton $k Button]] ""]} {
 	set b$k $v
     }
 }
 destroy .fakebutton
 
-option add *Roster.cbackground           $tbackground       widgetDefault
-option add *Roster.groupindent           22                 widgetDefault
-option add *Roster.jidindent             24                 widgetDefault
-option add *Roster.jidmultindent         40                 widgetDefault
-option add *Roster.subjidindent          34                 widgetDefault
-option add *Roster.groupiconindent        2                 widgetDefault
-option add *Roster.subgroupiconindent    22                 widgetDefault
-option add *Roster.iconindent             3                 widgetDefault
-option add *Roster.subitemtype            1                 widgetDefault
-option add *Roster.subiconindent         13                 widgetDefault
-option add *Roster.textuppad              1	            widgetDefault
-option add *Roster.textdownpad            1	            widgetDefault
-option add *Roster.linepad                2	            widgetDefault
-option add *Roster.foreground            $tforeground       widgetDefault
-option add *Roster.jidfill               $tbackground       widgetDefault
-option add *Roster.jidhlfill             $bactiveBackground widgetDefault
-option add *Roster.jidborder             $tbackground       widgetDefault
-option add *Roster.groupfill             $bbackground       widgetDefault
-option add *Roster.groupcfill            $bbackground       widgetDefault
-option add *Roster.grouphlfill           $bactiveBackground widgetDefault
-option add *Roster.groupborder           $tforeground       widgetDefault
-option add *Roster.connectionfill        $tbackground       widgetDefault
-option add *Roster.connectioncfill       $tbackground       widgetDefault
-option add *Roster.connectionhlfill      $bactiveBackground widgetDefault
-option add *Roster.connectionborder      $tforeground       widgetDefault
+option add *Roster.cbackground            $tbackground       widgetDefault
+option add *Roster.groupindent            22                 widgetDefault
+option add *Roster.jidindent              24                 widgetDefault
+option add *Roster.jidmultindent          40                 widgetDefault
+option add *Roster.subjidindent           34                 widgetDefault
+option add *Roster.groupiconindent         2                 widgetDefault
+option add *Roster.subgroupiconindent     22                 widgetDefault
+option add *Roster.iconindent              3                 widgetDefault
+option add *Roster.subitemtype             1                 widgetDefault
+option add *Roster.subiconindent          13                 widgetDefault
+option add *Roster.textuppad               1                 widgetDefault
+option add *Roster.textdownpad             1                 widgetDefault
+option add *Roster.linepad                 2                 widgetDefault
+option add *Roster.foreground             $tforeground       widgetDefault
+option add *Roster.jidfill                $tbackground       widgetDefault
+option add *Roster.jidhlfill              $bactiveBackground widgetDefault
+option add *Roster.jidborder              $tbackground       widgetDefault
+option add *Roster.metajidfill            $bbackground       widgetDefault
+option add *Roster.metajidhlfill          $bactiveBackground widgetDefault
+option add *Roster.metajidborder          $bbackground       widgetDefault
+option add *Roster.groupfill              $bbackground       widgetDefault
+option add *Roster.groupcfill             $bbackground       widgetDefault
+option add *Roster.grouphlfill            $bactiveBackground widgetDefault
+option add *Roster.groupborder            $tforeground       widgetDefault
+option add *Roster.connectionfill         $tbackground       widgetDefault
+option add *Roster.connectioncfill        $tbackground       widgetDefault
+option add *Roster.connectionhlfill       $bactiveBackground widgetDefault
+option add *Roster.connectionborder       $tforeground       widgetDefault
 option add *Roster.unsubscribedforeground #663333            widgetDefault
-option add *Roster.unavailableforeground #666666            widgetDefault
-option add *Roster.dndforeground         #666633            widgetDefault
-option add *Roster.xaforeground          #004d80            widgetDefault
-option add *Roster.awayforeground        #004d80            widgetDefault
-option add *Roster.availableforeground   #0066cc            widgetDefault
-option add *Roster.chatforeground        #0099cc            widgetDefault
+option add *Roster.unavailableforeground  #666666            widgetDefault
+option add *Roster.dndforeground          #666633            widgetDefault
+option add *Roster.xaforeground           #004d80            widgetDefault
+option add *Roster.awayforeground         #004d80            widgetDefault
+option add *Roster.availableforeground    #0066cc            widgetDefault
+option add *Roster.chatforeground         #0099cc            widgetDefault
 
 unset tbackground tforeground bbackground bactiveBackground
 
 namespace eval roster {
     custom::defgroup Roster [::msgcat::mc "Roster options."] -group Tkabber
-    custom::defvar use_aliases 1 \
-	[::msgcat::mc "Use aliases to show multiple users in one roster item."] \
-	-type boolean -group Roster \
-	-command [namespace current]::redraw_after_idle
     custom::defvar show_only_online 0 \
 	[::msgcat::mc "Show only online users in roster."] \
 	-type boolean -group Roster \
@@ -183,7 +183,7 @@
 		lappend tmp [lrange $tmp1 0 $i]
 	    }
 	}
-	set groups [lrmdups $tmp]
+	set groups [lsort -unique $tmp]
     }
 
     set c($jid) {}
@@ -263,21 +263,35 @@
     redraw_after_idle
 }
 
-# TODO: get rid of roster::roster
+proc roster::filter_match {xlib jid} {
+    variable options
+
+    if {$options(use_filter) && $options(filter) != ""} {
+	if {[string first [string tolower $options(filter)] \
+			  [string tolower [::roster::get_label $xlib $jid]]] < 0} {
+	    if {!$options(match_jids) || \
+		    [string first [string tolower $options(filter)] \
+				  [string tolower $jid]] < 0} {
+		return 0
+	    }
+	}
+    }
+
+    return 1
+}
+
 proc roster::redraw {} {
-    upvar #0 roster::aliases aliases
     variable roster
     variable options
     variable config
     variable show_only_online
-    variable use_aliases
     variable show_transport_user_icons
     variable undef_group_name
     variable chats_group_name
     variable own_resources_group_name
 
     clear .roster 0
-    
+
     set connections [connections]
     switch -- [llength $connections] {
 	0 {
@@ -286,140 +300,189 @@
 	}
 	1 {
 	    set draw_connection 0
-	    set gindent 0
 	}
 	default {
 	    set draw_connection 1
-	    set gindent 0
 	}
     }
 
     foreach xlib $connections {
-        # Suppress display of aliases, but not if "main" JID is unavailable.
-        # If main JID is unavailable, perform reordering of aliases.
-        # Suppression means that aliases wont get a line in the roster for
-	# themselves, but will be places as children under "main" JID element
-	if {$use_aliases} {
-	    foreach jid [array names aliases] {
-		set status [get_user_status $xlib $jid]
-		if {$status == "unavailable"} {
-		    # Need to look an alternative for main JID,
-		    # which is offline.
-		    set jidstatus [get_user_aliases_status_and_jid $xlib $jid]
-                    switch -- [lindex $jidstatus 1] {
-                        unavailable {
-                            # Main JID and all aliases are unavailable.
-			    # Will skip them all
-                        }
-                        default {
-                            # Make "most available" JID the main JID,
-			    # put main JID into list of aliases
-                            set new_jid [lindex $jidstatus 0]
-                            set idx [lsearch -exact $aliases($jid) $new_jid]
-                            set new_aliases [lreplace $aliases($jid) $idx $idx]
-                            # If this already have some aliases defined,
-			    # merge them in
-                            if {[info exists aliases($new_jid)]} {
-                                set new_aliases \
-				    [lsort -unique -dictionary -index 0 \
-					 [lappend new_aliases \
-					      $aliases($new_jid)]]
-                            }
-                            set aliases($new_jid) $new_aliases
-                            # Remove aliases of old "main" JID
-                            set aliases($jid) {}
-                        }
-                    }
-                }
-                # Main JID is online, suppress alternatives
-                foreach alias $aliases($jid) {
-                    set ignore_jid($alias) ""
-                }
-            }
-        }
-        
+
+	# TODO: Move to a plugin
+	# Preparing metacontacts.
+	array unset metajids
+	array unset metagroups
+	set metacontacted {}
+	if {[namespace exists ::plugins::metacontacts]} {
+	    foreach tag [::plugins::metacontacts::get_all_tags $xlib] {
+		set jids [::plugins::metacontacts::get_jids $xlib $tag]
+		set metagroups($tag) {}
+
+		foreach jid $jids {
+		    # Skip JID if it doesn't match filter pattern or if it
+		    # isn't a JID of use
+		    set isuser [::roster::itemconfig $xlib $jid -isuser]
+		    if {$isuser == ""} {
+			set isuser 1
+		    }
+		    if {$isuser && [filter_match $xlib $jid]} {
+			lappend metagroups($tag) $jid
+			lappend metacontacted $jid
+		    }
+		}
+
+		if {[llength $metagroups($tag)] == 0} continue
+
+		set mjid [lindex $metagroups($tag) 0]
+		set pjid [get_jid_of_user $xlib $mjid]
+		set priority [get_jid_presence_info priority $xlib $pjid]
+		set status [get_jid_status $xlib $pjid]
+
+		foreach rjid [lrange $metagroups($tag) 1 end] {
+		    set jid  [get_jid_of_user $xlib $rjid]
+		    set stat [get_jid_status $xlib $jid]
+
+		    if {[string equal $stat unavailable]} continue
+
+		    set prio [get_jid_presence_info priority $xlib $jid]
+		    if {$prio == ""} {
+			set prio 0
+		    }
+
+		    if {$prio > $priority || \
+			    ($prio == $priority && [compare_status $stat $status] > 0)} {
+			set mjid $rjid
+			set pjid $jid
+			set status $stat
+			set priority $prio
+		    }
+		}
+
+		lappend metajids($mjid) $tag
+	    }
+
+	    foreach jid [array names metajids] {
+		set metajids($jid) [lsort -unique $metajids($jid)]
+	    }
+	    set metacontacted [lsort -unique $metacontacted]
+	}
+
+	# Draw connection group
 	if {$draw_connection} {
-	    if {![info exists roster(collapsed,[list xlib $xlib])]} {
+	    if {![info exists roster(collapsed,[list ::xlib:: $xlib])]} {
 		set roster(collapsed,[list xlib $xlib]) 0
 	    }
 	    addline .roster connection \
-		[connection_jid $xlib] \
-		[list xlib $xlib] [list xlib $xlib] 0
-	    
+			    [connection_jid $xlib] \
+			    [list xlib $xlib]
+			    [list xlib $xlib]
+			    {} 0
+
 	    if {$roster(collapsed,[list xlib $xlib])} {
 		continue
 	    }
 	}
 
-	if {[lempty [::roster::get_jids $xlib]]} {
+	# Don't draw roster if it doesn't contain items
+	if {[llength [::roster::get_jids $xlib]] == 0} {
 	    continue
 	}
-	set bare_jid [connection_bare_jid $xlib]
+
+	# List of pairs {"group string for sorting", "group split on delimiter"}
 	set groups {}
+
+	# Array of lists of JIDs in group
 	array unset jidsingroup
+
+	# Array of lists of JIDs in group descendants
 	array unset jidsundergroup
+
+	# Array of lists of subgroups
 	array unset groupsundergroup
+
+	array unset jstat
+	array unset useronline
+
 	foreach jid [::roster::get_jids $xlib] {
-	    if {$options(use_filter) && $options(filter) != ""} {
-		if {[string first [string tolower $options(filter)] \
-				  [string tolower [::roster::get_label $xlib $jid]]] < 0} {
-		    if {!$options(match_jids) || \
-			    [string first [string tolower $options(filter)] \
-					  [string tolower $jid]] < 0} {
-			continue
-		    }
-		}
-	    }
-	    if {[info exists ignore_jid($jid)]} continue
+	    # Skip JID if it doesn't match filter pattern
+	    if {![filter_match $xlib $jid]} continue
+
+	    # TODO: Move to a plugin
+	    # Skip JID if it should be ignored because of metacontacts
+	    if {[lsearch -exact $metacontacted $jid] >= 0 && \
+		    ![info exists metajids($jid)]} continue
+
+	    # Add JID groups to a groups list
 	    set jid_groups [::roster::itemconfig $xlib $jid -group]
-	    if {![lempty $jid_groups]} {
+	    if {[llength $jid_groups] > 0} {
 		foreach group $jid_groups {
 		    if {$options(nested)} {
 			set sgroup [msplit $group $options(nested_delimiter)]
 		    } else {
 			set sgroup [list $group]
 		    }
+
+		    # Use the fact that -dictionary sorting puts \u0000 before
+		    # any other character, so subgroups will be placed just
+		    # after their parent group
 		    lappend groups [list [join $sgroup "\u0000"] $sgroup]
+
 		    lappend jidsingroup($sgroup) $jid
-		    set deep [expr {[llength $sgroup] - 1}]
-		    for {set i 0} {$i < $deep} {incr i} {
+
+		    if {![info exists jidsundergroup($sgroup)]} {
+			set jidsundergroup($sgroup) {}
+		    }
+
+		    if {![info exists groupsundergroup($sgroup)]} {
+			set groupsundergroup($sgroup) {}
+		    }
+
+		    for {set i [expr {[llength $sgroup] - 2}]} {$i >= 0} {incr i -1} {
+			# Adding all parent groups to a group list
 			set sgr [lrange $sgroup 0 $i]
+
 			lappend groups [list [join $sgr "\u0000"] $sgr]
+
 			lappend jidsundergroup($sgr) $jid
+
 			lappend groupsundergroup($sgr) $sgroup
+
 			if {![info exists jidsingroup($sgr)]} {
 			    set jidsingroup($sgr) {}
 			}
 		    }
-		    if {![info exists jidsundergroup($sgroup)]} {
-			set jidsundergroup($sgroup) {}
-		    }
-		    if {![info exists groupsundergroup($sgroup)]} {
-			set groupsundergroup($sgroup) {}
-		    }
 		}
 	    } else {
 		set sgroup [list $undef_group_name]
+
 		lappend jidsingroup($sgroup) $jid
-		set groupsundergroup($sgroup) {}
+
 		if {![info exists jidsundergroup($sgroup)]} {
 		    set jidsundergroup($sgroup) {}
 		}
+
+		set groupsundergroup($sgroup) {}
 	    }
 	}
+
 	set groups [lsort -unique -dictionary -index 0 $groups]
+
+	# FIXME: What to do with subgroups of $undef_group_name?
+	# Putting undefined group to the and of list
 	set ugroup [list $undef_group_name]
 	if {[info exists jidsingroup($ugroup)]} {
 	    lappend groups [list [join $ugroup "\u0000"] $ugroup]
 	}
+
+	# Putting active chats group to the beginning
 	if {$options(chats_group)} {
 	    set cgroup [list $chats_group_name]
 	    foreach chatid [chat::opened $xlib] {
 		set jid [chat::get_jid $chatid]
 		lappend jidsingroup($cgroup) $jid
-		if {[cequal [roster::itemconfig $xlib $jid -isuser] ""]} {
+		if {[string equal [roster::itemconfig $xlib $jid -isuser] ""]} {
 		    roster::itemconfig $xlib $jid \
-			-name [chat::get_nick $xlib $jid chat]
+			    -name [chat::get_nick $xlib $jid chat]
 		    roster::itemconfig $xlib $jid -subsc none
 		}
 	    }
@@ -429,6 +492,8 @@
 	    set groupsundergroup($cgroup) {}
 	    set jidsundergroup($cgroup) {}
 	}
+
+	# Putting own resources group to the beginning
 	if {$options(show_own_resources)} {
 	    set cgroup [list $own_resources_group_name]
 	    set jid [::xmpp::jid::normalize [connection_bare_jid $xlib]]
@@ -438,6 +503,9 @@
 	    set groupsundergroup($cgroup) {}
 	    set jidsundergroup($cgroup) {}
 	}
+
+	# Info on whether to show offline users in a group is needed for
+	# subgroups too, so an extra loop
 	foreach group $groups {
 	    set group [lindex $group 1]
 	    set gid [list $xlib $group]
@@ -445,18 +513,31 @@
 		set roster(show_offline,$gid) 0
 	    }
 	}
+
+	# Drawing groups an JIDs in them
 	foreach group $groups {
 	    set group [lindex $group 1]
-	    set jidsingroup($group) [lrmdups $jidsingroup($group)]
-	    set groupsundergroup($group) [lrmdups $groupsundergroup($group)]
 	    set gid [list $xlib $group]
+
+	    set jidsingroup($group) [lsort -unique $jidsingroup($group)]
+	    set groupsundergroup($group) [lsort -unique $groupsundergroup($group)]
+
 	    if {![info exists roster(collapsed,$gid)]} {
 		set roster(collapsed,$gid) 0
 	    }
+
+	    # How to indent the group (also, the number of its ancestors)
 	    set indent [expr {[llength $group] - 1}]
+
+	    # Whether to draw group at all
 	    set collapse 0
+
+	    # Whether to show offline users in the group
 	    set show_offline_users 0
+
+	    # Whether to show the group ???
 	    set show_offline_group 0
+
 	    foreach undergroup $groupsundergroup($group) {
 		if {$roster(show_offline,[list $xlib $undergroup])} {
 		    set show_offline_group 1
@@ -466,27 +547,32 @@
 	    for {set i 0} {$i < $indent} {incr i} {
 		set sgr [list $xlib [lrange $group 0 $i]]
 		if {$roster(collapsed,$sgr)} {
+		    # Whether some ancestor is collapsed
 		    set collapse 1
 		    break
 		}
 		if {$roster(show_offline,$sgr)} {
+		    # Whether showing offline users is required for some
+		    # ancestor
 		    set show_offline_users 1
 		    set show_offline_group 1
 		}
 	    }
-	    incr indent $gindent
+
+	    # If some ancestor is collapsed don't draw the group
 	    if {$collapse} continue
-	    set group_name "[lindex $group end]"
+
+	    set group_label [lindex $group end]
 	    set online 0
 	    set users 0
 	    set not_users 0
-	    set sub_jids 0
 	    foreach jid [concat $jidsingroup($group) $jidsundergroup($group)] {
 		if {[::roster::itemconfig $xlib $jid -isuser]} {
 		    incr users
-		    set status [get_user_aliases_status $xlib $jid]
-		    set jstat($jid) $status
-		    if {$status != "unavailable"} {
+		    if {![info exists jstat($jid)]} {
+			set jstat($jid) [get_user_status $xlib $jid]
+		    }
+		    if {$jstat($jid) != "unavailable"} {
 			incr online
 			set useronline($jid) 1
 		    } else {
@@ -496,100 +582,145 @@
 		    incr not_users
 		}
 	    }
+
+	    # Draw group label
 	    if {!$show_only_online || $show_offline_group || \
 		    $roster(show_offline,$gid) || \
 		    ($options(use_filter) && $options(filter) != "") || \
-		    $online + $not_users + $sub_jids > 0} {
-		if {$users} {
-		    addline .roster group "$group_name ($online/$users)" \
-			$gid $gid $indent
-		} else {
-		    addline .roster group $group_name \
-			$gid $gid $indent
+		    $online + $not_users > 0} {
+		if {$users > 0} {
+		    append group_label " ($online/$users)"
 		}
+		addline .roster group $group_label $gid $gid {} $indent
 	    }
+
+	    # Draw group contents if it isn't collapsed
 	    if {!$roster(collapsed,$gid)} {
-		set jid_names {}
+		set jid_labels {}
 		foreach jid $jidsingroup($group) {
-		    lappend jid_names [list $jid [::roster::get_label $xlib $jid]]
+		    lappend jid_labels [list $jid [::roster::get_label $xlib $jid]]
 		}
-		set jid_names [lsort -index 1 -dictionary $jid_names]
-		foreach jid_name $jid_names {
-		    lassign $jid_name jid name
+		set jid_labels [lsort -index 1 -dictionary $jid_labels]
+
+		foreach jid_label $jid_labels {
+		    lassign $jid_label jid label
+
 		    if {$options(chats_group)} {
 			set chatid [chat::chatid $xlib $jid]
 			if {[info exists chat::chats(messages,$chatid)] && \
 				$chat::chats(messages,$chatid) > 0} {
-			    append name " \[$chat::chats(messages,$chatid)\]"
+			    append label " ($chat::chats(messages,$chatid))"
 			}
 		    }
+
 		    set cjid [list $xlib $jid]
 		    if {!$show_only_online || $show_offline_users || $roster(show_offline,$gid) || \
 			    ($options(use_filter) && $options(filter) != "") || \
 			    ![info exists useronline($jid)] || $useronline($jid)} {
-			lassign [::roster::get_category_and_subtype $xlib $jid] category type
-			set jids [get_jids_of_user $xlib $jid]
-			set numjids [llength $jids]
-			if {($numjids > 1) && ($config(subitemtype) > 0) && \
-				$category == "user"} {
+
+			if {[info exists metajids($jid)]} {
+
+			    set jids {}
+			    foreach tag $metajids($jid) {
+				set jids [concat $jids $metagroups($tag)]
+			    }
+			    set jids [lsort -unique $jids]
+			    set numjids [llength $jids]
 			    if {$config(subitemtype) & 1} {
-				if {$category == "conference"} {
-				    set numjids [expr {$numjids - 1}]
-				}
-				set label "$name ($numjids)"
-			    } else {
-				set label "$name"
+				append label " ($numjids)"
 			    }
-			    addline .roster jid $label $cjid $gid $indent $jids
-			    changeicon .roster $cjid [get_jid_icon $xlib $jid]
-			    changeforeground .roster $cjid [get_jid_foreground $xlib $jid]
-			    
-			    if {!$roster(collapsed,$cjid)} {
+			    addline .roster metajid $label $cjid $gid \
+					    [list $xlib $metajids($jid)] $indent $jids \
+					    [get_jid_icon $xlib $jid] \
+					    [get_jid_foreground $xlib $jid]
+
+			    if {!$roster(metacollapsed,[list $xlib $metajids($jid)])} {
+				set subjid_labels {}
 				foreach subjid $jids {
-				    set subjid_resource [::xmpp::jid::resource $subjid]
-				    if {$subjid_resource != ""} {
-					addline .roster jid2 \
-					    $subjid_resource [list $xlib $subjid] \
-					    $gid $indent \
-					    [list $subjid]
-					changeicon .roster \
-					    [list $xlib $subjid] [get_jid_icon $xlib $subjid]
-					changeforeground .roster \
-					    [list $xlib $subjid] [get_jid_foreground $xlib $subjid]
-				    }
+				    lappend subjid_labels \
+					    [list $subjid [::roster::get_label $xlib $subjid]]
 				}
+				set subjid_labels [lsort -index 1 -dictionary $subjid_labels]
+			    
+				foreach subjid_label $subjid_labels {
+				    lassign $subjid_label subjid label
+
+				    draw_jid $xlib $subjid $label $gid \
+					     [list $xlib $metajids($jid)] $indent jstat
+				}
 			    }
 			} else {
-			    if {$numjids <= 1 && $category == "user" && \
-				    !$show_transport_user_icons} {
-				if {[info exists jstat($jid)]} {
-				    set status $jstat($jid)
-				} else {
-				    set status [get_user_aliases_status $xlib $jid]
-				}
-				set subsc [::roster::itemconfig $xlib $jid -subsc]
-				if {([cequal $subsc from] || [cequal $subsc none]) && \
-					$status == "unavailable"} {
-				    set status unsubscribed
-				}
-				addline .roster jid $name $cjid $gid $indent \
-				    $jids \
-				    roster/user/$status \
-				    $config(${status}foreground)
-			    } else {
-				addline .roster jid $name $cjid $gid $indent $jids
-				changeicon .roster $cjid [get_jid_icon $xlib $jid]
-				changeforeground .roster $cjid [get_jid_foreground $xlib $jid]
-			    }
+			    draw_jid $xlib $jid $label $gid {} $indent jstat
 			}
 		    }
 		}
 	    }
 	}
     }
+
     update_scrollregion .roster
 }
 
+proc roster::draw_jid {xlib jid label gid metajids indent jstatVar} {
+    variable config
+    variable roster
+    variable show_transport_user_icons
+    upvar $jstatVar jstat
+
+    set cjid [list $xlib $jid]
+    lassign [::roster::get_category_and_subtype $xlib $jid] category type
+
+    set jids [get_jids_of_user $xlib $jid]
+    set numjids [llength $jids]
+
+    if {$category == "user" && $numjids > 1 && $config(subitemtype) > 0} {
+
+	if {$config(subitemtype) & 1} {
+	    append label " ($numjids)"
+	}
+	addline .roster jid $label $cjid $gid $metajids $indent $jids \
+			[get_jid_icon $xlib $jid] \
+			[get_jid_foreground $xlib $jid]
+
+	if {!$roster(jidcollapsed,$cjid)} {
+	    foreach subjid $jids {
+		set subjid_resource [::xmpp::jid::resource $subjid]
+		if {$subjid_resource != ""} {
+		    addline .roster jid2 \
+				    $subjid_resource [list $xlib $subjid] \
+				    $gid $metajids $indent \
+				    [list $subjid] \
+				    [get_jid_icon $xlib $subjid] \
+				    [get_jid_foreground $xlib $subjid]
+		}
+	    }
+	}
+    } elseif {$category == "user" && $numjids <= 1 && !$show_transport_user_icons} {
+
+	if {[info exists jstat($jid)]} {
+	    set status $jstat($jid)
+	} else {
+	    set status [get_user_status $xlib $jid]
+	}
+
+	set subsc [::roster::itemconfig $xlib $jid -subsc]
+	if {([string equal $subsc from] || [string equal $subsc none]) && \
+		$status == "unavailable"} {
+	    set status unsubscribed
+	}
+	addline .roster jid $label $cjid $gid $metajids $indent $jids \
+			roster/user/$status \
+			$config(${status}foreground)
+    } else {
+	if {$category == "conference" && ($config(subitemtype) & 1) && $numjids > 1} {
+	    append label " ([incr numjids -1])"
+	}
+	addline .roster jid $label $cjid $gid $metajids $indent $jids \
+			[get_jid_icon $xlib $jid] \
+			[get_jid_foreground $xlib $jid]
+    }
+}
+
 proc roster::redraw_after_idle {args} {
     variable redraw_afterid
 
@@ -608,47 +739,19 @@
 }
 
 proc roster::get_jids_of_user {xlib user} {
-    upvar #0 roster::aliases aliases
-    variable use_aliases
-
-    if {$use_aliases && [info exists aliases($user)]} {
-	set jids [::get_jids_of_user $xlib $user]
-	foreach alias $aliases($user) {
-	    set jids [concat $jids [::get_jids_of_user $xlib $alias]]
-	}
-	return $jids
-    } else {
-	return [::get_jids_of_user $xlib $user]
-    }
+    # TODO: metacontacts
+    return [::get_jids_of_user $xlib $user]
 }
 
-proc roster::get_user_aliases_status {xlib user} {
-    set jidstatus [get_user_aliases_status_and_jid $xlib $user]
-    return [lindex $jidstatus 1]
-}
+proc roster::get_foreground {status} {
+    variable config
 
-proc roster::get_user_aliases_status_and_jid {xlib user} {
-    upvar #0 roster::aliases aliases
-    variable use_aliases
-
-    if {$use_aliases && [info exists aliases($user)]} {
-	set status [get_user_status $xlib $user]
-        set jid $user
-
-	foreach alias $aliases($user) {
-	    set new_status [max_status $status [get_user_status $xlib $alias]]
-            if {$status != $new_status} {
-                set status $new_status
-                set jid $alias
-	}
-	}
-	return [list $jid $status]
-    } else {
-	return [list $user [get_user_status $xlib $user]]
-    }
+    return $config(${status}foreground)
 }
 
 proc roster::get_jid_foreground {xlib jid} {
+    variable config
+
     lassign [::roster::get_category_and_subtype $xlib $jid] category type
 
     switch -- $category {
@@ -658,9 +761,9 @@
 	}
 	conference {
 	    if {[get_jid_status $xlib $jid] != "unavailable"} {
-		return available
+		return $config(availableforeground)
 	    } else {
-		return unavailable
+		return $config(unavailableforeground)
 	    }
 	}
 	server  -
@@ -669,36 +772,44 @@
 	    return [get_service_foreground $xlib $jid $type]
 	}
 	default {
-	    return ""
+	    return $config(foreground)
 	}
     }
 }
 
 proc roster::get_service_foreground {xlib service type} {
+    variable config
+
     switch -- $type {
-	jud {return ""}
+	jud {
+	    return $config(foreground)
 	}
-    if {![cequal [::roster::itemconfig $xlib $service -subsc] none]} {
-	return [get_user_status $xlib $service]
+    }
+
+    if {![string equal [::roster::itemconfig $xlib $service -subsc] none]} {
+	set status [get_user_status $xlib $service]
+	return $config(${status}foreground)
     } else {
-	return unsubscribed
+	return $config(unsubscribedforeground)
     }
 }
 
 proc roster::get_user_foreground {xlib user} {
-    set status [get_user_aliases_status $xlib $user]
+    variable config
 
+    set status [get_user_status $xlib $user]
+
     set subsc [::roster::itemconfig $xlib $user -subsc]
-    if {[cequal $subsc ""]} {
+    if {[string equal $subsc ""]} {
 	set subsc [::roster::itemconfig $xlib \
 		       [::roster::find_jid $xlib $user] -subsc]
     }
 
-    if {([cequal $subsc from] || [cequal $subsc none]) && \
+    if {([string equal $subsc from] || [string equal $subsc none]) && \
 	    $status == "unavailable"} {
-	return unsubscribed
+	return $config(unsubscribedforeground)
     } else {
-	return $status
+	return $config(${status}foreground)
     }
 }
 
@@ -709,7 +820,7 @@
 	"" -
 	user {
 	    if {$status == ""} {
-		set status [get_user_aliases_status $xlib $jid]
+		set status [get_user_status $xlib $jid]
 	    }
 	    return [get_user_icon $xlib $jid $status]
 	}
@@ -747,7 +858,7 @@
 	    jud {return services/jud}
 	    sms {return services/sms}
 	}
-	if {![cequal [::roster::itemconfig $xlib $service -subsc] none]} {
+	if {![string equal [::roster::itemconfig $xlib $service -subsc] none]} {
 	    if {![catch { image type services/$type/$status }]} {
 		return services/$type/$status
 	    } else {
@@ -757,7 +868,7 @@
 	    return roster/user/unsubscribed
 	}
     } else {
-	if {![cequal [::roster::itemconfig $xlib $service -subsc] none]} {
+	if {![string equal [::roster::itemconfig $xlib $service -subsc] none]} {
 	    return roster/user/$status
 	} else {
 	    return roster/user/unsubscribed
@@ -769,12 +880,12 @@
     variable show_transport_user_icons
 
     set subsc [::roster::itemconfig $xlib $user -subsc]
-    if {[cequal $subsc ""]} {
+    if {[string equal $subsc ""]} {
 	set subsc [::roster::itemconfig $xlib \
 	               [::roster::find_jid $xlib $user] -subsc]
     }
 
-    if {!([cequal $subsc from] || [cequal $subsc none]) || \
+    if {!([string equal $subsc from] || [string equal $subsc none]) || \
 	    $status != "unavailable"} {
 	if {$show_transport_user_icons} {
 	    set service [::xmpp::jid::server $user]
@@ -808,14 +919,10 @@
     $c itemconfigure jid$tag&&icon -image $icon
 }
 
-proc roster::changeforeground {w jid fg {color ""}} {
-    variable config
-
+proc roster::changeforeground {w jid color} {
     set c $w.canvas
     set tag [jid_to_tag $jid]
-    if {$color == ""} {
-	set color $config(${fg}foreground)
-    }
+
     $c itemconfigure jid$tag&&text -fill $color
 }
 
@@ -824,7 +931,7 @@
     variable config
 
     set c $w.canvas
-    
+
     set width 150
     set height 100
     set popupproc {}
@@ -849,41 +956,44 @@
     set sw [ScrolledWindow $w.sw]
     pack $sw -fill both -expand yes
 
-    set config(groupindent)        [option get $w groupindent Roster] 
-    set config(jidindent)          [option get $w jidindent   Roster]
-    set config(jidmultindent)      [option get $w jidmultindent   Roster]
-    set config(jid2indent)         [option get $w subjidindent Roster]
-    set config(groupiconindent)    [option get $w groupiconindent  Roster]
-    set config(subgroupiconindent) [option get $w subgroupiconindent  Roster]
-    set config(iconindent)         [option get $w iconindent  Roster]
-    set config(subitemtype)        [option get $w subitemtype  Roster]
-    set config(subiconindent)      [option get $w subiconindent  Roster]
-    set config(textuppad)          [option get $w textuppad   Roster]
-    set config(textdownpad)        [option get $w textdownpad Roster]
-    set config(linepad)	           [option get $w linepad     Roster]
-    set config(background)  [option get $w cbackground Roster] 
-    set config(jidfill)	    [option get $w jidfill     Roster]
-    set config(jidhlfill)   [option get $w jidhlfill   Roster]
-    set config(jidborder)   [option get $w jidborder   Roster]
-    set config(jid2fill)    $config(jidfill)
-    set config(jid2hlfill)  $config(jidhlfill)
-    set config(jid2border)  $config(jidborder)
-    set config(groupfill)   [option get $w groupfill   Roster]
-    set config(groupcfill)  [option get $w groupcfill  Roster]
-    set config(grouphlfill) [option get $w grouphlfill Roster]
-    set config(groupborder) [option get $w groupborder Roster]
-    set config(connectionfill)   [option get $w connectionfill   Roster]
-    set config(connectioncfill)  [option get $w connectioncfill  Roster]
-    set config(connectionhlfill) [option get $w connectionhlfill Roster]
-    set config(connectionborder) [option get $w connectionborder Roster]
-    set config(foreground)             [option get $w foreground  Roster]
+    set config(groupindent)            [option get $w groupindent            Roster]
+    set config(jidindent)              [option get $w jidindent              Roster]
+    set config(jidmultindent)          [option get $w jidmultindent          Roster]
+    set config(jid2indent)             [option get $w subjidindent           Roster]
+    set config(groupiconindent)        [option get $w groupiconindent        Roster]
+    set config(subgroupiconindent)     [option get $w subgroupiconindent     Roster]
+    set config(iconindent)             [option get $w iconindent             Roster]
+    set config(subitemtype)            [option get $w subitemtype            Roster]
+    set config(subiconindent)          [option get $w subiconindent          Roster]
+    set config(textuppad)              [option get $w textuppad              Roster]
+    set config(textdownpad)            [option get $w textdownpad            Roster]
+    set config(linepad)	               [option get $w linepad                Roster]
+    set config(background)             [option get $w cbackground            Roster]
+    set config(metajidfill)	       [option get $w metajidfill            Roster]
+    set config(metajidhlfill)          [option get $w metajidhlfill          Roster]
+    set config(metajidborder)          [option get $w metajidborder          Roster]
+    set config(jidfill)	               [option get $w jidfill                Roster]
+    set config(jidhlfill)              [option get $w jidhlfill              Roster]
+    set config(jidborder)              [option get $w jidborder              Roster]
+    set config(jid2fill)               $config(jidfill)
+    set config(jid2hlfill)             $config(jidhlfill)
+    set config(jid2border)             $config(jidborder)
+    set config(groupfill)              [option get $w groupfill              Roster]
+    set config(groupcfill)             [option get $w groupcfill             Roster]
+    set config(grouphlfill)            [option get $w grouphlfill            Roster]
+    set config(groupborder)            [option get $w groupborder            Roster]
+    set config(connectionfill)         [option get $w connectionfill         Roster]
+    set config(connectioncfill)        [option get $w connectioncfill        Roster]
+    set config(connectionhlfill)       [option get $w connectionhlfill       Roster]
+    set config(connectionborder)       [option get $w connectionborder       Roster]
+    set config(foreground)             [option get $w foreground             Roster]
     set config(unsubscribedforeground) [option get $w unsubscribedforeground Roster]
-    set config(unavailableforeground)  [option get $w unavailableforeground Roster]
-    set config(dndforeground)          [option get $w dndforeground Roster]
-    set config(xaforeground)           [option get $w xaforeground Roster]
-    set config(awayforeground)         [option get $w awayforeground Roster]
-    set config(availableforeground)    [option get $w availableforeground Roster]
-    set config(chatforeground)         [option get $w chatforeground Roster]
+    set config(unavailableforeground)  [option get $w unavailableforeground  Roster]
+    set config(dndforeground)          [option get $w dndforeground          Roster]
+    set config(xaforeground)           [option get $w xaforeground           Roster]
+    set config(awayforeground)         [option get $w awayforeground         Roster]
+    set config(availableforeground)    [option get $w availableforeground    Roster]
+    set config(chatforeground)         [option get $w chatforeground         Roster]
 
     canvas $c -bg $config(background) \
 	-highlightthickness $::tk_highlightthickness \
@@ -912,177 +1022,242 @@
     if {[info exists dropcmd]} {
 	lappend args -dropcmd $dropcmd
     }
-    if {![lempty $args]} {
+    if {[llength $args] > 0} {
 	eval [list DropSite::register $c -droptypes {JID}] $args
     }
 }
 
-proc roster::addline {w type text jid group indent {jids {}} {icon ""} {foreground ""}} {
-    upvar #0 roster::aliases aliases
+proc roster::addline {w type text jid group metajids indent {jids {}} {icon ""} {foreground ""}} {
     variable roster
     variable iroster
     variable config
-    variable use_aliases
 
     set c $w.canvas
 
     set tag [jid_to_tag $jid]
     set grouptag [jid_to_tag $group]
+    set metatag [jid_to_tag $metajids]
 
     set ypad 1
     set linespace [font metric $::RosterFont -linespace]
     set lineheight [expr {$linespace + $ypad}]
 
     set uy $iroster($w,ypos)
-    set ly [expr {$uy + $lineheight + $config(textuppad) + \
-		      $config(textdownpad)}]
+    set ly [expr {$uy + $lineheight + $config(textuppad) + $config(textdownpad)}]
 
     set levindent [expr $config(groupindent)*$indent]
 
+    if {[string equal $metajids {}]} {
+	set metaindent 0
+    } else {
+	set metaindent $config(groupindent)
+    }
+
     set border $config(${type}border)
     set hlfill $config(${type}hlfill)
 
-    if {($type == "group" || $type == "connection") && \
-	[info exists roster(collapsed,$jid)] && \
-	$roster(collapsed,$jid)} {
+    if {([string equal $type group] || [string equal $type connection]) && \
+	    [info exists roster(collapsed,$jid)] && $roster(collapsed,$jid)} {
 	set rfill $config(${type}cfill)
     } else {
 	set rfill $config(${type}fill)
     }
-    
-    if {$type == "connection"} {
+
+    if {[string equal $type connection]} {
 	set type group
     }
 
-    $c create rectangle [expr {1 + $levindent}] $uy 10000 $ly -fill $rfill \
-	-outline $border -tags [list jid$tag group$grouptag $type rect]
+    $c create rectangle [expr {1 + $levindent}] $uy 10000 $ly \
+	      -fill $rfill \
+	      -outline $border \
+	      -tags [list jid$tag group$grouptag meta$metatag $type rect]
 
+    switch -- $type {
+	metajid {
+	    set isuser 1
+	    set y [expr {($uy + $ly)/2}]
+	    set x [expr {$config(iconindent) + $levindent}]
 
-    if {[cequal $type jid]} {
-	lassign $jid xlib jjid
-	set isuser [::roster::itemconfig $xlib $jjid -isuser]
-	if {[cequal $isuser ""]} {
-	    set isuser 1
+	    if {$icon == ""} {
+		set icon roster/user/unavailable
+	    }
+	    $c create image $x $y -image $icon \
+				  -anchor w \
+				  -tags [list jid$tag group$grouptag meta$metatag $type icon]
+
+	    if {[llength $jids] > 1} {
+		if {[info exists roster(metacollapsed,$metajids)] && \
+			!$roster(metacollapsed,$metajids)} {
+		    set jid_state opened
+		} else {
+		    set roster(metacollapsed,$metajids) 1
+		    set jid_state closed
+		}
+		if {$config(subitemtype) > 0 && ($config(subitemtype) & 2)} {
+		    set y [expr {($uy + $ly)/2}]
+		    set x [expr {$config(subgroupiconindent) + $levindent}]
+		    $c create image $x $y -image roster/group/$jid_state \
+					  -anchor w \
+					  -tags [list jid$tag group$grouptag meta$metatag $type group]
+		}
+	    } else {
+		set roster(metacollapsed,$metajids) 1
+	    }
 	}
+	jid {
+	    lassign $jid xlib jjid
+	    set isuser [::roster::itemconfig $xlib $jjid -isuser]
+	    if {[string equal $isuser ""]} {
+		set isuser 1
+	    }
 
-	set y [expr {($uy + $ly)/2}]
-	set x [expr {$config(iconindent) + $levindent}]
+	    set y [expr {($uy + $ly)/2}]
+	    set x [expr {$config(iconindent) + $levindent + $metaindent}]
 
-	if {$icon == ""} {
-	    $c create image $x $y -image roster/user/unavailable \
-		-anchor w \
-		-tags [list jid$tag group$grouptag $type icon]
-	} else {
+	    if {$icon == ""} {
+		set icon roster/user/unavailable
+	    }
 	    $c create image $x $y -image $icon \
-		-anchor w \
-		-tags [list jid$tag group$grouptag $type icon]
-	}
-	if {[llength $jids] > 1} {
-	    if {[info exists roster(collapsed,$jid)] && !$roster(collapsed,$jid)} {
-		set jid_state opened
-	    } else {
-		set roster(collapsed,$jid) 1
-		set jid_state closed
-	    }
-	    if {$config(subitemtype) > 0} {
-		if {($config(subitemtype) & 2) && $isuser} {
+				  -anchor w \
+				  -tags [list jid$tag group$grouptag meta$metatag $type icon]
+
+	    if {[llength $jids] > 1} {
+		if {[info exists roster(jidcollapsed,$jid)] && !$roster(jidcollapsed,$jid)} {
+		    set jid_state opened
+		} else {
+		    set roster(jidcollapsed,$jid) 1
+		    set jid_state closed
+		}
+		if {$config(subitemtype) > 0 && ($config(subitemtype) & 2) && $isuser} {
 		    set y [expr {($uy + $ly)/2}]
-		    set x [expr {$config(subgroupiconindent) + $levindent}]
-		    $c create image $x $y -image roster/group/$jid_state -anchor w \
-			-tags [list jid$tag group$grouptag $type group]
+		    set x [expr {$config(subgroupiconindent) + $levindent + $metaindent}]
+		    $c create image $x $y -image roster/group/$jid_state \
+					  -anchor w \
+					  -tags [list jid$tag group$grouptag meta$metatag $type group]
 		}
+	    } else {
+		set roster(jidcollapsed,$jid) 1
 	    }
-	} else {
-	    set roster(collapsed,$jid) 1
 	}
-    } elseif {[cequal $type jid2]} {
-	#set jids [get_jids_of_user $jid]
-	set y [expr {($uy + $ly)/2}]
-	set x [expr {$config(subiconindent) + $levindent}]
+	jid2 {
+	    set y [expr {($uy + $ly)/2}]
+	    set x [expr {$config(subiconindent) + $levindent + $metaindent}]
 
-	$c create image $x $y -image roster/user/unavailable -anchor w \
-	    -tags [list jid$tag group$grouptag $type icon]
-    } elseif {[cequal $type group]} {
-	set y [expr {($uy + $ly)/2}]
-	set x [expr {$config(groupiconindent) + $levindent}]
-	if {[info exists roster(collapsed,$jid)] && $roster(collapsed,$jid)} {
-	    set group_state closed
-	} else {
-	    set group_state opened
+	    if {$icon == ""} {
+		set icon roster/user/unavailable
+	    }
+	    $c create image $x $y -image $icon \
+				  -anchor w \
+				  -tags [list jid$tag group$grouptag meta$metatag $type icon]
 	}
-	$c create image $x $y -image roster/group/$group_state -anchor w \
-	    -tags [list jid$tag group$grouptag $type icon]
+	group {
+	    set y [expr {($uy + $ly)/2}]
+	    set x [expr {$config(groupiconindent) + $levindent}]
+	    if {[info exists roster(collapsed,$jid)] && $roster(collapsed,$jid)} {
+		set group_state closed
+	    } else {
+		set group_state opened
+	    }
+	    $c create image $x $y -image roster/group/$group_state \
+				  -anchor w \
+				  -tags [list jid$tag group$grouptag meta$metatag $type icon]
+	}
     }
 
-    if {([cequal $type jid]) && ($config(subitemtype) > 0) && ($config(subitemtype) & 2)} {
-	#set jids [get_jids_of_user $jid]
-	if {$isuser && ([llength $jids] > 1)} {
-	    set x [expr {$config(jidmultindent) + $levindent}]
-	} else {
-	    set x [expr {$config(jidindent) + $levindent}]
+    switch -- $type {
+	metajid -
+	jid {
+	    if {($config(subitemtype) > 0) && ($config(subitemtype) & 2) && \
+		    $isuser && ([llength $jids] > 1)} {
+		set x [expr {$config(jidmultindent) + $levindent}]
+	    } else {
+		set x [expr {$config(jidindent) + $levindent}]
+	    }
 	}
-    } else {
-	set x [expr {$config(${type}indent) + $levindent}]
+	default {
+	    set x [expr {$config(${type}indent) + $levindent}]
+	}
     }
+    switch -- $type {
+	jid -
+	jid2 {
+	    incr x $metaindent
+	}
+    }
+
     incr uy $config(textuppad)
 
-    if {$foreground == ""} {
-	if {[cequal $type jid] || [cequal $type jid2]} {
-	    set foreground $config(unavailableforeground)
-	} else {
-	    set foreground $config(foreground)
+    if {[string equal $foreground ""]} {
+	switch -- $type {
+	    metajid -
+	    jid -
+	    jid2 {
+		set foreground $config(unavailableforeground)
+	    }
+	    default {
+		set foreground $config(foreground)
+	    }
 	}
     }
-    $c create text $x $uy -text $text -anchor nw -font $::RosterFont \
-	-fill $foreground -tags [list jid$tag group$grouptag $type text]
+    $c create text $x $uy -text $text \
+			  -anchor nw \
+			  -font $::RosterFont \
+			  -fill $foreground \
+			  -tags [list jid$tag group$grouptag meta$metatag $type text]
 
     set iroster($w,width) [max $iroster($w,width) \
-			      [expr {$x + [font measure $::RosterFont $text]}]]
+			       [expr {$x + [font measure $::RosterFont $text]}]]
 
+    $c bind jid$tag&&$type <Any-Enter> \
+	    [list $c itemconfig jid$tag&&$type&&rect -fill $hlfill]
+    $c bind jid$tag&&$type <Any-Leave> \
+	    [list $c itemconfig jid$tag&&$type&&rect -fill $rfill]
 
-    $c bind jid$tag <Any-Enter> \
-	[list $c itemconfig jid$tag&&rect -fill $hlfill]
-    $c bind jid$tag <Any-Leave> \
-	[list $c itemconfig jid$tag&&rect -fill $rfill]
-
     set doubledjid  [double% $jid]
     set doubledjids [double% $jids]
 
     set iroster($w,ypos) [expr {$ly + $config(linepad)}]
 
-    if {[cequal $type jid] || [cequal $type jid2]} {
-	$c bind jid$tag <Button-1> \
-	    [list [namespace current]::on_singleclick \
-		 [double% $iroster($w,singleclick)] $doubledjid]
+    switch -- $type {
+	metajid -
+	jid -
+	jid2 {
+	    $c bind jid$tag&&$type <Button-1> \
+		    [list [namespace current]::on_singleclick \
+			  [double% $iroster($w,singleclick)] \
+			  $c %x %y $doubledjid $doubledjids]
 
-	$c bind jid$tag <Double-Button-1> \
-	    [list [namespace current]::on_doubleclick \
-		 [double% $iroster($w,doubleclick)] $doubledjid]
+	    $c bind jid$tag&&$type <Double-Button-1> \
+		    [list [namespace current]::on_doubleclick \
+			  [double% $iroster($w,doubleclick)] $doubledjid $doubledjids]
 
-	$c bind jid$tag <Any-Enter> \
-	    +[list eval balloon::set_text \
-		  \[[namespace current]::jids_popup_info \
-		  [list $doubledjid] [list $doubledjids]\]]
+	    $c bind jid$tag&&$type <Any-Enter> \
+		    +[list eval balloon::set_text \
+			   \[[namespace current]::jids_popup_info \
+			   [list $doubledjid] [list $doubledjids]\]]
 
-	$c bind jid$tag <Any-Motion> \
-	    [list eval balloon::on_mouse_move \
-		 \[[namespace current]::jids_popup_info \
-		 [list $doubledjid] [list $doubledjids]\] %X %Y]
+	    $c bind jid$tag&&$type <Any-Motion> \
+		    [list eval balloon::on_mouse_move \
+			  \[[namespace current]::jids_popup_info \
+			  [list $doubledjid] [list $doubledjids]\] %X %Y]
 
-	$c bind jid$tag <Any-Leave> {+ balloon::destroy}
-	
-	if {![cequal $iroster($w,popup) {}]} {
-	    $c bind jid$tag <3> [list [double% $iroster($w,popup)] $doubledjid]
+	    $c bind jid$tag&&$type <Any-Leave> {+ balloon::destroy}
+
+	    if {![string equal $iroster($w,popup) ""]} {
+		$c bind jid$tag&&$type <3> [list [double% $iroster($w,popup)] \
+						 $doubledjid $doubledjids]
+	    }
 	}
-    } else {
-	if {$w == ".roster"} {
-	    $c bind jid$tag&&group <Button-1> \
-		[list [namespace current]::group_click $doubledjid]
-	}
+	default {
+	    if {$w == ".roster"} {
+		$c bind jid$tag&&group <Button-1> \
+		        [list [namespace current]::group_click $doubledjid]
+	    }
 
-	if {![cequal $iroster($w,grouppopup) {}]} {
-	    $c bind jid$tag&&group <3> \
-		[list $iroster($w,grouppopup) $doubledjid]
+	    if {![string equal $iroster($w,grouppopup) {}]} {
+		$c bind jid$tag&&group <3> \
+		        [list $iroster($w,grouppopup) $doubledjid]
+	    }
 	}
     }
 }
@@ -1108,31 +1283,35 @@
 
 ###############################################################################
 
-proc roster::on_singleclick {command cjid} {
+proc roster::on_singleclick {command c x y cjid jids} {
     variable click_afterid
 
     if {$command == ""} return
 
+    set xc [$c canvasx $x]
+    set yc [$c canvasy $y]
+    set tags [$c gettags [lindex [$c find closest $xc $yc] 0]]
+
     if {![info exists click_afterid]} {
 	set click_afterid \
-	    [after 300 [list [namespace current]::singleclick_run $command $cjid]]
+	    [after 300 [list [namespace current]::singleclick_run $command $tags $cjid $jids]]
     } else {
 	after cancel $click_afterid
 	unset click_afterid
     }
 }
 
-proc roster::singleclick_run {command cjid} {
+proc roster::singleclick_run {command tags cjid jids} {
     variable click_afterid
 
     if {[info exists click_afterid]} {
 	unset click_afterid
     }
 
-    eval $command {$cjid}
+    eval $command [list $tags $cjid $jids]
 }
 
-proc roster::on_doubleclick {command cjid} {
+proc roster::on_doubleclick {command cjid jids} {
     variable click_afterid
 
     if {[info exists click_afterid]} {
@@ -1142,12 +1321,12 @@
 
     if {$command == ""} return
 
-    eval $command {$cjid}
+    eval $command [list $cjid $jids]
 }
 
 ###############################################################################
 
-proc roster::jid_doubleclick {id} {
+proc roster::jid_doubleclick {id ids} {
     lassign $id xlib jid
     lassign [::roster::get_category_and_subtype $xlib $jid] category subtype
 
@@ -1179,16 +1358,11 @@
 }
 
 proc roster::jids_popup_info {id jids} {
-    variable use_aliases
-
     lassign $id xlib jid
 
+    # TODO: metacontacts
     if {$jids == {}} {
-	if {$use_aliases && [info exists aliases($jid)]} {
-	    set jids [concat [list $jid] $aliases($jid)]
-	} else {
-	    set jids [list $jid]
-	}
+	set jids [list $jid]
     }
 
     set text {}
@@ -1221,7 +1395,7 @@
 	default {
 	    set status $statusdesc([get_user_status $xlib $user])
 	    set desc   [get_user_status_desc $xlib $user]
-	    if {[cequal $category1 conference] && $i > 0} {
+	    if {[string equal $category1 conference] && $i > 0} {
 		if {$options(show_conference_user_info)} {
 		    set name "     [::xmpp::jid::resource $user]"
 		} else {
@@ -1231,16 +1405,16 @@
 	}
     }
 
-    if {(![string equal -nocase  $status $desc]) && (![cequal $desc ""])} {
+    if {(![string equal -nocase  $status $desc]) && (![string equal $desc ""])} {
 	append status " ($desc)"
     }
 
     set subsc [::roster::itemconfig $xlib $bare_user -subsc]
-    if {($options(show_subscription) && ![cequal $subsc ""]) &&
-	    !([cequal $category1 conference] && [cequal $category user])} {
+    if {($options(show_subscription) && ![string equal $subsc ""]) &&
+	    !([string equal $category1 conference] && [string equal $category user])} {
 	set subsc [format "\n\t[::msgcat::mc {Subscription:}] %s" $subsc]
 	set ask [::roster::itemconfig $xlib $bare_user -ask]
-	if {![cequal $ask ""]} {
+	if {![string equal $ask ""]} {
 	    set ask [format "  [::msgcat::mc {Ask:}] %s" $ask]
 	}
     } else {
@@ -1250,7 +1424,7 @@
 
     set user_popup_info "$name: $status$subsc$ask"
 
-    if {!([cequal $category1 conference] && $i > 0) || \
+    if {!([string equal $category1 conference] && $i > 0) || \
 	    $options(show_conference_user_info)} {
 	hook::run roster_user_popup_info_hook \
 	    [namespace which -variable user_popup_info] $xlib $user
@@ -1267,7 +1441,7 @@
 
 proc roster::is_online {xlib jid} {
     if {[::roster::itemconfig $xlib $jid -isuser]} {
-	switch -- [get_user_aliases_status $xlib $jid] {
+	switch -- [get_user_status $xlib $jid] {
 	    unavailable {return 0}
 	    default {return 1}
 	}
@@ -1340,20 +1514,20 @@
 
     set tags [$c gettags [lindex [$c find closest $xc $yc] 0]]
 
-    if {$options(free_drop) && ![cequal $tags ""]} {
-	lassign [tag_to_jid [crange [lindex $tags 1] 5 end]] xlib gr
+    if {$options(free_drop) && ![string equal $tags ""]} {
+	lassign [tag_to_jid [string range [lindex $tags 1] 5 end]] xlib gr
 	if {$xlib == "xlib"} {
 	    set xlib $gr
 	    set gr {}
 	}
-    } elseif {[lcontain $tags group]} {
-	lassign [tag_to_jid [crange [lindex $tags 0] 3 end]] xlib gr
+    } elseif {[lsearch -exact $tags group] >= 0} {
+	lassign [tag_to_jid [string range [lindex $tags 0] 3 end]] xlib gr
 	if {$xlib == "xlib"} {
 	    set xlib $gr
 	    set gr {}
 	}
-    } elseif {![cequal $tags ""]} {
-	lassign [tag_to_jid [crange [lindex $tags 1] 5 end]] xlib
+    } elseif {![string equal $tags ""]} {
+	lassign [tag_to_jid [string range [lindex $tags 1] 5 end]] xlib
 	set gr {}
     } else {
 	set xlib [lindex [connections] 0]
@@ -1371,15 +1545,15 @@
     set subsc ""
 
     if {[info exists fromgid]} {
-	lassign $fromgid fromxlib fromgr 
+	lassign $fromgid fromxlib fromgr
 	if {$options(nested)} {
 	    set fromgr [join $fromgr $options(nested_delimiter)]
 	} else {
 	    set fromgr [lindex $fromgr 0]
 	}
-    }	
+    }
 
-    if {![lcontain [::roster::get_jids $xlib] $jid]} {
+    if {[lsearch -exact [::roster::get_jids $xlib] $jid] < 0} {
 	if {$gr != {}} {
 	    set groups [list $gr]
 	} else {
@@ -1408,7 +1582,7 @@
 	}
 	if {$gr != ""} {
 	    lappend groups $gr
-	    set groups [lrmdups $groups]
+	    set groups [lsort -unique $groups]
 	    debugmsg roster $groups
 	}
 
@@ -1426,10 +1600,10 @@
     set c .roster.canvas
 
     set tags [$c gettags current]
-    if {[lcontain $tags jid]} {
-	set grouptag [crange [lindex $tags 1] 5 end]
+    if {[lsearch -exact $tags jid] >= 0} {
+	set grouptag [string range [lindex $tags 1] 5 end]
 	set gid [tag_to_jid $grouptag]
-	set tag [crange [lindex $tags 0] 3 end]
+	set tag [string range [lindex $tags 0] 3 end]
 	set cjid [tag_to_jid $tag]
 	lassign $cjid xlib jid
 
@@ -1448,21 +1622,32 @@
 
 ###############################################################################
 
-proc roster::user_singleclick {cjid} {
+proc roster::user_singleclick {tags cjid jids} {
     variable roster
 
     lassign $cjid xlib jid
+    set type [lindex $tags 3]
+    set cmeta [tag_to_jid [string range [lindex $tags 2] 4 end]]
 
-    if {[roster::itemconfig $xlib $jid -isuser] && \
-	    [llength [get_jids_of_user $xlib $jid]] > 1} {
-	set roster(collapsed,$cjid) [expr {!$roster(collapsed,$cjid)}]
-	redraw_after_idle
+    switch -- $type {
+	metajid {
+	    if {[llength $jids] > 1} {
+		set roster(metacollapsed,$cmeta) [expr {!$roster(metacollapsed,$cmeta)}]
+		redraw_after_idle
+	    }
+	}
+	default {
+	    if {[roster::itemconfig $xlib $jid -isuser] && [llength $jids] > 1} {
+		set roster(jidcollapsed,$cjid) [expr {!$roster(jidcollapsed,$cjid)}]
+		redraw_after_idle
+	    }
+	}
     }
 }
 
 ###############################################################################
 
-proc roster::popup_menu {id} {
+proc roster::popup_menu {id jids} {
     global curuser
 
     lassign $id xlib jid
@@ -1471,7 +1656,7 @@
     lassign [::roster::get_category_and_subtype $xlib $jid] category subtype
 
     switch -- $category {
-	user       {set menu [create_user_menu $xlib $jid]}
+	user       {set menu [create_user_menu $xlib $jid $jids]}
 	conference {set menu [conference_popup_menu $xlib $jid]}
 	server  -
 	gateway -
@@ -1509,12 +1694,21 @@
 
 ###############################################################################
 
-proc roster::create_user_menu {xlib user} {
+proc roster::create_user_menu {xlib user jids} {
     set m .jidpopupmenu
     if {[winfo exists $m]} { destroy $m }
     menu $m -tearoff 0
 
-    set jids [get_jids_of_user $xlib $user]
+    set jids1 {}
+    foreach jid $jids {
+	set resources [get_jids_of_user $xlib $jid]
+	if {[llength $resources] == 0} {
+	    lappend jids1 $jid
+	} else {
+	    set jids1 [concat $jids1 [get_jids_of_user $xlib $jid]]
+	}
+    }
+    set jids $jids1
 
     switch -- [llength $jids] {
 	0 {
@@ -1539,7 +1733,7 @@
 		set m1 .jidpopupmenu[jid_to_tag $jid]
 		if {[winfo exists $m1]} { destroy $m1 }
 	    }
- 
+
 	    return $m
 	}
     }
@@ -1575,10 +1769,9 @@
     set command_list {}
     foreach jid $jids {
 	set m1 $prefix[jid_to_tag $jid]$suffix
-	set idx [$m1 index $label]
-	if {$idx != "none"} {
+	if {![catch {set idx [$m1 index $label]}] && $idx != "none"} {
 	    set command [$m1 entrycget $idx -command]
-	    if {![lcontain $command_list $command]} {
+	    if {[lsearch -exact $command_list $command] < 0} {
 		lappend command_list $command
 	    }
 	}
@@ -1588,8 +1781,7 @@
 	$m add cascade -label $label -menu $m2
 	foreach jid $jids {
 	    set m1 $prefix[jid_to_tag $jid]$suffix
-	    set idx [$m1 index $label]
-	    if {$idx != "none"} {
+	    if {![catch {set idx [$m1 index $label]}] && $idx != "none"} {
 		set command [$m1 entrycget $idx -command]
 		$m2 add command -label $jid \
 		    -command [string map [list $m1 $m2] $command]
@@ -1606,11 +1798,10 @@
     set command_list {}
     foreach jid $jids {
 	set m1 $prefix[jid_to_tag $jid]$suffix
-	set idx [$m1 index $label]
-	if {$idx != "none"} {
+	if {![catch {set idx [$m1 index $label]}] && $idx != "none"} {
 	    set var [$m1 entrycget $idx -variable]
 	    set command [$m1 entrycget $idx -command]
-	    if {![lcontain $command_list [list $var $command]]} {
+	    if {[lsearch -exact $command_list [list $var $command]] < 0} {
 		lappend command_list [list $var $command]
 	    }
 	}
@@ -1620,8 +1811,7 @@
 	$m add cascade -label $label -menu $m2
 	foreach jid $jids {
 	    set m1 $prefix[jid_to_tag $jid]$suffix
-	    set idx [$m1 index $label]
-	    if {$idx != "none"} {
+	    if {![catch {set idx [$m1 index $label]}] && $idx != "none"} {
 		set var [$m1 entrycget $idx -variable]
 		set command [$m1 entrycget $idx -command]
 		$m2 add checkbutton -label $jid -variable $var -command $command
@@ -1806,7 +1996,7 @@
     if {[winfo exists $w]} {
 	destroy $w
     }
-    
+
     Dialog $w -title [::msgcat::mc "Rename roster group"] \
 	-separator 1 -anchor e -default 0 -cancel 1
 
@@ -1815,7 +2005,7 @@
     $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w]
 
     set p [$w getframe]
-    
+
     label $p.lgroupname -text [::msgcat::mc "New group name:"]
     ecursor_entry [entry $p.groupname -textvariable new_roster_group_name]
 
@@ -1846,7 +2036,7 @@
 	unset roster(show_offline,$gid)
     }
 }
-	
+
 proc roster::add_group_by_jid_regexp_dialog {} {
     global new_roster_group_rname
     global new_roster_group_regexp
@@ -1855,7 +2045,7 @@
     if {[winfo exists $w]} {
 	destroy $w
     }
-    
+
     Dialog $w -title [::msgcat::mc "Add roster group by JID regexp"] \
 	-separator 1 -anchor e -default 0 -cancel 1
 
@@ -1867,7 +2057,7 @@
     $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w]
 
     set p [$w getframe]
-    
+
     label $p.lgroupname -text [::msgcat::mc "New group name:"]
     ecursor_entry [entry $p.groupname -textvariable new_roster_group_rname]
     label $p.lregexp -text [::msgcat::mc "JID regexp:"]

Modified: trunk/tkabber/muc.tcl
===================================================================
--- trunk/tkabber/muc.tcl	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/muc.tcl	2009-02-13 17:23:25 UTC (rev 1649)
@@ -641,6 +641,7 @@
     $vf configure -height $h
 
     $w draw
+    $sf yview moveto 1.0
 }
 
 ###############################################################################
@@ -790,6 +791,7 @@
 	-width 30
     grid $f.jid$row -row $row -column 1 -sticky we -padx 1m
     bindscroll $f.jid$row $sf
+    focus $f.jid$row
 
     switch -- $attr {
 	role {

Modified: trunk/tkabber/plugins/roster/conferences.tcl
===================================================================
--- trunk/tkabber/plugins/roster/conferences.tcl	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/plugins/roster/conferences.tcl	2009-02-13 17:23:25 UTC (rev 1649)
@@ -28,13 +28,12 @@
 #   </iq>
 #
 
-package require xmpp::private
+package require xmpp::roster::bookmarks
 
 namespace eval conferences {
     # variable to store roster conference bookmarks
     array set bookmarks {}
 
-    set ::NS(bookmarks) "storage:bookmarks"
     set ::NS(tkabber:groups) "tkabber:bookmarks:groups"
 }
 
@@ -63,18 +62,17 @@
     set responds($xlib) 0
     array unset bookmarks $xlib,*
 
-    ::xmpp::private::retrieve $xlib [list [::xmpp::xml::create storage \
-						-xmlns $::NS(bookmarks)]] \
+    ::xmpp::roster::bookmarks::retrieve $xlib \
 	    -command [list [namespace current]::process_bookmarks $xlib]
 
     ::xmpp::private::retrieve $xlib [list [::xmpp::xml::create storage \
 						-xmlns $::NS(tkabber:groups)]] \
-	    -command [list [namespace current]::process_bookmarks $xlib]
+	    -command [list [namespace current]::process_groups $xlib]
 }
 
 hook::add connected_hook [namespace current]::conferences::request_bookmarks 20
 
-proc conferences::process_bookmarks {xlib status xmlList} {
+proc conferences::process_bookmarks {xlib status bmlist} {
     variable bookmarks
     variable responds
     global NS
@@ -83,20 +81,33 @@
 
     incr responds($xlib)
 
+    foreach bookmark $bmlist {
+	create_muc_bookmark $xlib $bookmark
+    }
+
+    if {$responds($xlib) < 2} return
+
+    push_bookmarks_to_roster $xlib
+    after idle [list [namespace current]::autojoin_groups $xlib]
+}
+
+proc conferences::process_groups {xlib status xmlList} {
+    variable bookmarks
+    variable responds
+    global NS
+
+    if {$status != "ok"} return
+
+    incr responds($xlib)
+
     foreach xml $xmlList {
 	::xmpp::xml::split $xml tag xmlns attrs cdata subels
 
-	switch -- $xmlns \
-	    $NS(bookmarks) {
-		foreach bookmark $subels {
-		    create_muc_bookmark $xlib $bookmark
-		}
-	    } \
-	    $NS(tkabber:groups) {
-		foreach bookmark $subels {
-		    create_muc_bmgroup $xlib $bookmark
-		}
+	if {$xmlns == $NS(tkabber:groups)} {
+	    foreach bookmark $subels {
+		create_muc_bmgroup $xlib $bookmark
 	    }
+	}
     }
 
     if {$responds($xlib) < 2} return
@@ -105,7 +116,7 @@
     after idle [list [namespace current]::autojoin_groups $xlib]
 }
 
-proc conferences::create_muc_bookmark {xlib xmldata args} {
+proc conferences::create_muc_bookmark {xlib bookmark args} {
     variable bookmarks
 
     set merge 0
@@ -118,20 +129,33 @@
 	}
     }
 
-    ::xmpp::xml::split $xmldata tag xmlns attrs cdata subels
+    array set n $bookmark
 
-    if {![string equal $tag conference]} { return 0 }
+    set jid [::xmpp::jid::normalize $n(jid)]
 
-    set jid [string tolower [::xmpp::xml::getAttr $attrs jid]]
-
     if {$merge && [info exists bookmarks($xlib,jid,$jid)]} {
 	return 0
     } else {
 	set bookmarks($xlib,jid,$jid) $jid
 
-	set bookmarks($xlib,name,$jid) [::xmpp::xml::getAttr $attrs name]
+	set bookmarks($xlib,name,$jid) ""
 	set bookmarks($xlib,nick,$jid) ""
 	set bookmarks($xlib,password,$jid) ""
+	set bookmarks($xlib,autojoin,$jid) 0
+
+	if {[info exists n(name)]} {
+	    set bookmarks($xlib,name,$jid) $n(name)
+	}
+	if {[info exists n(nick)]} {
+	    set bookmarks($xlib,nick,$jid) $n(nick)
+	}
+	if {[info exists n(password)]} {
+	    set bookmarks($xlib,password,$jid) $n(password)
+	}
+	if {[info exists n(autojoin)]} {
+	    set bookmarks($xlib,autojoin,$jid) $n(autojoin)
+	}
+
 	if {![info exists bookmarks($xlib,groups,$jid)]} {
 	    set bookmarks($xlib,groups,$jid) {}
 	    set bookmarks($xlib,hasgroups,$jid) 0
@@ -139,20 +163,6 @@
 	    set bookmarks($xlib,hasgroups,$jid) 1
 	}
 
-	set autojoin [::xmpp::xml::getAttr $attrs autojoin]
-	switch -- $autojoin {
-	    1 -
-	    true { set bookmarks($xlib,autojoin,$jid) 1 }
-	    default { set bookmarks($xlib,autojoin,$jid) 0 }
-	}
-	
-	foreach subel $subels {
-	    ::xmpp::xml::split $subel stag sxmlns sattrs scdata ssubels
-	    switch -- $stag {
-		nick { set bookmarks($xlib,nick,$jid) $scdata }
-		password { set bookmarks($xlib,password,$jid) $scdata }
-	    }
-	}
 	return 1
     }
 }
@@ -175,7 +185,7 @@
 
     if {![string equal $tag conference]} return
 
-    set jid [string tolower [::xmpp::xml::getAttr $attrs jid]]
+    set jid [::xmpp::jid::normalize [::xmpp::xml::getAttr $attrs jid]]
 
     set groups [list]
     foreach subel $subels {
@@ -224,18 +234,15 @@
 	set autojoin $bookmarks($xlib,autojoin,$jid)
 	
 	set vars [list jid $jid name $name autojoin $autojoin]
-	set subtags {}
+
 	if {$bookmarks($xlib,nick,$jid) != ""} {
-	    lappend subtags [::xmpp::xml::create nick \
-				    -cdata $bookmarks($xlib,nick,$jid)]
+	    lappend vars nick $bookmarks($xlib,nick,$jid)
 	}
 	if {$bookmarks($xlib,password,$jid) != ""} {
-	    lappend subtags [::xmpp::xml::create password \
-				    -cdata $bookmarks($xlib,password,$jid)]
+	    lappend vars password $bookmarks($xlib,password,$jid)
 	}
-	lappend bookmarklist [::xmpp::xml::create conference \
-				    -attrs $vars \
-				    -subelements $subtags]
+	lappend bookmarklist $vars
+
 	set vars [list jid $jid]
 	set groups {}
 	foreach group $bookmarks($xlib,groups,$jid) {
@@ -247,12 +254,10 @@
 				-subelements $groups]
     }
 
-    list [::xmpp::xml::create storage \
-		-xmlns $::NS(bookmarks) \
-		-subelements $bookmarklist] \
-	 [::xmpp::xml::create storage \
-		-xmlns $::NS(tkabber:groups) \
-		-subelements $grouplist]
+    return [list $bookmarklist \
+		 [::xmpp::xml::create storage \
+			    -xmlns $::NS(tkabber:groups) \
+			    -subelements $grouplist]]
 }
 
 proc conferences::store_bookmarks {xlib args} {
@@ -267,10 +272,10 @@
 	}
     }
 
-    foreach item [serialize_bookmarks $xlib] {
-	::xmpp::private::store $xlib [list $item] \
-		-command $command
-    }
+    lassign [serialize_bookmarks $xlib] bookmarks groups
+
+    ::xmpp::roster::bookmarks::store $xlib $bookmarks -command $command
+    ::xmpp::private::store $xlib [list $groups] -command $command
 }
 
 proc conferences::store_bookmarks_result {xlib res child} {
@@ -439,7 +444,7 @@
 
     destroy $gw
 
-    set jid [string tolower ${gra_group}@$gra_server]
+    set jid [::xmpp::jid::normalize ${gra_group}@$gra_server]
     if {$gra_rostergroup == ""} {
 	set groups {}
     } else {

Modified: trunk/tkabber/utils.tcl
===================================================================
--- trunk/tkabber/utils.tcl	2009-02-11 17:53:04 UTC (rev 1648)
+++ trunk/tkabber/utils.tcl	2009-02-13 17:23:25 UTC (rev 1649)
@@ -503,7 +503,7 @@
 # $sep contains a Unicode character used to replace
 # found substrings before actual splitting;
 # this character MUST NOT occur in $s.
-proc msplit {s by {sep \u001F}} {
+proc msplit {s by {sep \u0000}} {
     split [string map [list $by $sep] $s] $sep
 }
 



More information about the Tkabber-dev mailing list