# $Id$ namespace eval message { variable msgid 0 variable headid 0 variable options custom::defvar message_dest_list {} \ [::msgcat::mc "List of message destination JIDs."] \ -group Hidden custom::defgroup Messages [::msgcat::mc "Message and Headline options."] \ -group Tkabber disco::register_feature jabber:x:oob } proc message::process_message {xlib from id type is_subject subject body err thread priority x} { switch -- $type { normal { show_dialog $xlib $from $id $type $subject $body $thread $priority $x return stop } } return } hook::add process_message_hook \ [namespace current]::message::process_message 98 proc message::show_dialog {xlib from id type subject body thread priority x {replyP 1}} { variable msgid if {$type == "normal" && $body != ""} { ::message_archive::log_message \ $from [connection_jid $xlib] \ $subject $body $x } set mw .msgshow[incr msgid] toplevel $mw -class Message wm group $mw . if {$replyP} { set title [::msgcat::mc "Message from %s" $from] } else { set title [::msgcat::mc "Extras from %s" $from] } wm title $mw $title wm iconname $mw $title wm withdraw $mw frame $mw.bottom pack $mw.bottom -side bottom -fill x if {$replyP} { set bbox [ButtonBox $mw.bottom.buttons -spacing 10 -padx 10] $bbox add -text [::msgcat::mc "Reply"] \ -command [list message::send $mw $xlib] $bbox add -text [::msgcat::mc "Chat"] \ -command "chat::open_to_user [list $xlib] [list $from] destroy $mw" $bbox add -text [::msgcat::mc "Close"] -command [list destroy $mw] bind $mw "ButtonBox::invoke [double% $bbox] 0" bind $mw "ButtonBox::invoke [double% $bbox] 2" pack $bbox -side right -fill x -padx 2m -pady 2m } else { ButtonBox $mw.bottom.buttons -spacing 0 -padx 10 $mw.bottom.buttons add -text [::msgcat::mc "Close"] -command [list destroy $mw] bind $mw "ButtonBox::invoke [double% $mw.bottom.buttons] 0" bind $mw "ButtonBox::invoke [double% $mw.bottom.buttons] 0" } pack $mw.bottom.buttons -side right -fill x -padx 2m -pady 2m set sep [Separator::create $mw.sep -orient horizontal] pack $sep -pady 1m -fill x -side bottom frame $mw.frame pack $mw.frame -side top -fill both -expand yes -padx 2m -pady 2m label $mw.thread -text $thread if {$replyP} { set title [::msgcat::mc "Message from:"] } else { set title [::msgcat::mc "Extras from:"] } frame $mw.title pack $mw.title -side top -anchor w -fill x -expand no -in $mw.frame grid columnconfigure $mw.title 1 -weight 1 if {[llength [connections]] > 1} { label $mw.title.lconnection -text [::msgcat::mc "Received by:"] label $mw.title.connection -text " [connection_jid $xlib]" grid $mw.title.lconnection -row 0 -column 0 -sticky e grid $mw.title.connection -row 0 -column 1 -sticky w } label $mw.title.lab -text $title menubutton $mw.title.mb -text $from -menu $mw.title.mb.menu menu $mw.title.mb.menu -tearoff 0 \ -postcommand [namespace code [list subject_menu $mw.title.mb.menu $xlib $from message]] grid $mw.title.lab -row 1 -column 0 -sticky e grid $mw.title.mb -row 1 -column 1 -sticky w foreach tag [bind Menubutton] { if {[string first 1 $tag] >= 0} { regsub -all 1 $tag 3 new bind $mw.title.mb $new [bind Menubutton $tag] } } frame $mw.tspace pack $mw.tspace -side top -fill x -pady 0.5m -in $mw.frame frame $mw.rf grid columnconfigure $mw.rf 1 -weight 1 set row 0 if {$replyP || (![cequal $subject ""])} { label $mw.rf.lsubj -text [::msgcat::mc "Subject:"] entry $mw.rf.subj $mw.rf.subj insert 0 $subject if {[info tclversion] >= 8.4} { set bgcolor [lindex [$mw.rf.subj configure -background] 4] $mw.rf.subj configure -state readonly -readonlybackground $bgcolor } else { $mw.rf.subj configure -state disabled } grid $mw.rf.lsubj -row 0 -column 0 -sticky e grid $mw.rf.subj -row 0 -column 1 -sticky ew incr row } pack $mw.rf -side top -anchor w -fill x -in $mw.frame frame $mw.rspace pack $mw.rspace -side top -fill x -in $mw.frame -pady 0.5m incr row set last $row hook::run message_process_x_hook row body \ $mw.rf $x $xlib $from $id $type $replyP if {(!$replyP) && ($row == $last)} { destroy $mw return } # Ignore messages with empty body and no (or unsupported, e.g. jabber:x:event) extras if {[cequal $body ""] && ($row == $last)} { destroy $mw return } ScrolledWindow $mw.rsw text $mw.rbody -width 60 -height 8 -wrap word ::richtext::config $mw.rbody -using {url emoticon stylecode} ::richtext::render_message $mw.rbody $body "" $mw.rbody configure -state disabled pack $mw.rsw -side top -fill both -expand yes -in $mw.frame $mw.rsw setwidget $mw.rbody if {$replyP} { button $mw.cite -text [::msgcat::mc "Quote"] -command [list message::quote $mw $body] pack $mw.cite -side top -anchor e -in $mw.frame frame $mw.f grid columnconfigure $mw.f 1 -weight 1 #label $mw.f.lto -text To: Entry $mw.f.to -dropenabled 1 -droptypes {JID {}} \ -dropcmd [list message::jiddropcmd] $mw.f.to insert 0 $from label $mw.f.lsubj -text [::msgcat::mc "Reply subject:"] entry $mw.f.subj regsub {(^Re: )*} $subject {Re: } subject $mw.f.subj insert 0 $subject #grid $mw.f.lto -row 0 -column 0 -sticky e #grid $mw.f.to -row 0 -column 1 -sticky ew grid $mw.f.lsubj -row 1 -column 0 -sticky e grid $mw.f.subj -row 1 -column 1 -sticky ew pack $mw.f -side top -anchor w -fill x -in $mw.frame frame $mw.space pack $mw.f -side top -anchor w -fill x -in $mw.frame -pady 1m ScrolledWindow $mw.sw pack $mw.sw -in $mw.frame -side top -fill both -expand yes textUndoable $mw.body -width 60 -height 8 -wrap word bind $mw.body "ButtonBox::invoke [double% $bbox] 0 break" $mw.sw setwidget $mw.body focus $mw.body hook::run open_message_post_hook $mw $xlib $from } BWidget::place $mw 0 0 center wm deiconify $mw } proc message::quote {mw body} { regsub -all {(^|\n)} $body {\1> } body $mw.body insert 0.0 $body $mw.body insert insert "\n" } proc message::process_x {rowvar bodyvar f x xlib from id type replyP} { upvar 2 $rowvar row upvar 2 $bodyvar body foreach xa $x { ::xmpp::xml::split $xa tag xmlns attrs cdata subels switch -- $xmlns { jabber:x:oob { set row [process_x_oob $f $subels $row $from] } jabber:x:data { if {[lindex $subels] > 0} { process_x_data $f $xlib $from $xa } } } } return } hook::add message_process_x_hook message::process_x 99 proc message::process_x_oob {f x row from} { set desc "" set url "" foreach item $x { ::xmpp::xml::split $item tag xmlns attrs cdata subels switch -- $tag { desc - url { set $tag $cdata } } } if {[cequal $url ""]} { return $row } if {[cequal $desc ""]} { set desc $url } label $f.luser$row -text [::msgcat::mc "Attached URL:"] render_url $f.user$row $url $desc -bg [get_conf $f -bg] grid $f.luser$row -row $row -column 0 -sticky e grid $f.user$row -row $row -column 1 -sticky ew incr row return $row } proc message::process_x_data {f xlib from x} { data::draw_window [list $x] [list message::send_x_data $xlib $from] } proc message::send_x_data {xlib to w restags} { ::xmpp::sendMessage $xlib $to -xlist $restags destroy $w } proc message::send_dialog_item {m xlib jid} { $m add command -label [::msgcat::mc "Send message..."] \ -command [list message::send_dialog \ -to $jid -connection $xlib] } hook::add roster_jid_popup_menu_hook message::send_dialog_item 15 hook::add message_dialog_menu_hook message::send_dialog_item 15 hook::add search_popup_menu_hook message::send_dialog_item 15 hook::add roster_create_groupchat_user_menu_hook message::send_dialog_item 15 package require sha1 proc message::generate_thread {from to} { return [sha1::sha1 $from$to[rand 1000000000]] } proc message::send_dialog {args} { variable msgid variable message_dest_list variable send_dialog_xlib if {[lempty [connections]]} return set to "" set subject "" set group 0 set cstate normal foreach {opt val} $args { switch -- $opt { -to { set to $val } -subject { set subject $val } -thread { set thread $val } -group { set group $val } -connection { set xlib $val set cstate disabled } } } if {![info exists xlib]} { set xlib [lindex [connections] 0] } set mw .msgsend[incr msgid] toplevel $mw -class Message wm group $mw . set send_dialog_xlib($mw) [connection_jid $xlib] if {![info exists thread]} { set thread [generate_thread $send_dialog_xlib($mw) $to] } if {$group} { if {$to != ""} { set title [::msgcat::mc "Send message to group %s" $to] } else { set title [::msgcat::mc "Send message to group"] } } else { if {$to != ""} { set title [::msgcat::mc "Send message to %s" $to] } else { set title [::msgcat::mc "Send message"] } } wm title $mw $title wm iconname $mw $title wm withdraw $mw #frame $mw.subj #label $mw.subj.lab -text Subject: #entry $mw.subj.entry #$mw.subj.entry insert 0 $subject #pack $mw.subj.lab $mw.subj.entry -side left #pack $mw.subj -side top -anchor w frame $mw.bottom pack $mw.bottom -side bottom -fill x set bbox [ButtonBox $mw.bottom.buttons -spacing 10 -padx 10] $bbox add -text [::msgcat::mc "Send"] \ -command [list message::send $mw $xlib -group $group] $bbox add -text [::msgcat::mc "Cancel"] -command [list destroy $mw] bind $mw "ButtonBox::invoke [double% $bbox] 0" bind $mw "ButtonBox::invoke [double% $bbox] 1" pack $bbox -side right -fill x -padx 2m -pady 2m set sep [Separator::create $mw.sep -orient horizontal] pack $sep -pady 1m -fill x -side bottom label $mw.thread -text $thread frame $mw.frame pack $mw.frame -side top -fill both -expand yes -padx 2m -pady 2m frame $mw.f grid columnconfigure $mw.f 1 -weight 1 set connections {} if {[llength [connections]] > 1} { foreach c [connections] { lappend connections [connection_jid $c] } label $mw.f.lconnection -text [::msgcat::mc "From: "] ComboBox $mw.f.connection \ -textvariable [namespace current]::send_dialog_xlib($mw) \ -values $connections \ -state $cstate grid $mw.f.lconnection -row 0 -column 0 -sticky e grid $mw.f.connection -row 0 -column 1 -sticky ew } if {$group} { # TODO reflect changes in xlib label $mw.f.lto -text [::msgcat::mc "Group: "] ComboBox $mw.f.to -text $to \ -values [roster::get_groups $xlib \ -nested $::ifacetk::roster::options(nested) \ -delimiter $::ifacetk::roster::options(nested_delimiter) \ -undefined 1] } else { label $mw.f.lto -text [::msgcat::mc "To: "] ComboBox $mw.f.to -text $to \ -dropenabled 1 -droptypes {JID {}} \ -dropcmd [list message::jiddropcmd] \ -values $message_dest_list } label $mw.f.lsubj -text [::msgcat::mc "Subject: "] entry $mw.f.subj $mw.f.subj insert 0 $subject grid $mw.f.lto -row 1 -column 0 -sticky e grid $mw.f.to -row 1 -column 1 -sticky ew grid $mw.f.lsubj -row 2 -column 0 -sticky e grid $mw.f.subj -row 2 -column 1 -sticky ew pack $mw.f -side top -anchor w -fill x -in $mw.frame frame $mw.space pack $mw.f -side top -anchor w -fill x -in $mw.frame -pady 1m ScrolledWindow $mw.sw pack $mw.sw -in $mw.frame -side top -fill both -expand yes textUndoable $mw.body -width 60 -height 8 -wrap word bind $mw.body "ButtonBox::invoke [double% $bbox] 0 break" $mw.sw setwidget $mw.body if {$to != ""} { focus $mw.f.subj } else { focus $mw.f.to } hook::run open_message_post_hook $mw $xlib $to BWidget::place $mw 0 0 center wm deiconify $mw } proc message::send0 {mw args} { variable send_dialog_xlib foreach c [connections] { if {[connection_jid $c] == $send_dialog_xlib($mw)} { set xlib $c } } unset send_dialog_xlib($mw) eval [list send $mw $xlib] $args } proc message::send {mw xlib args} { variable message_dest_list set group 0 foreach {opt val} $args { switch -- $opt { -group { set group $val } } } set jid [$mw.f.to cget -text] if {$group} { set jids [roster::get_group_jids $xlib $jid \ -nested $::ifacetk::roster::options(nested) \ -delimiter $::ifacetk::roster::options(nested_delimiter)] } else { set message_dest_list [update_combo_list $message_dest_list $jid 20] set jids [list $jid] } set thread [$mw.thread cget -text] set subj [$mw.f.subj get] set body [$mw.body get 1.0 {end -1 chars}] foreach jid $jids { if {!$group || [::roster::itemconfig $xlib $jid -isuser]} { send_msg $xlib $jid -type normal \ -subject $subj -body $body -thread $thread } } destroy $mw } proc message::send_msg {xlib to args} { array set params [list -xlist {}] array set params $args set command [list ::xmpp::sendMessage $xlib $to] set xs $params(-xlist) unset params(-xlist) if {[info exists params(-body)]} { set log_body $params(-body) foreach tag [list signed encrypted] { if {[cequal [info commands ::ssj::${tag}:output] ""]} { continue } if {[catch { ssj::${tag}:output $xlib $params(-body) $to } cdata]} { debugmsg message "ssj::${tag}:output: $cdata" return [list error ssj] } if {![cequal $cdata ""]} { lappend xs [::xmpp::xml::create x \ -xmlns jabber:x:$tag -cdata $cdata] if {[cequal $tag encrypted]} { set params(-body) [::msgcat::mc "This message is encrypted."] } } } } else { set log_body "" } if {[info exists params(-subject)]} { set log_subject $params(-subject) } else { set log_subject "" } if {[llength $xs] > 0} { lappend command -xlist $xs } foreach {k v} [array get params] { lappend command $k $v } eval $command if {(![info exists params(-type)] || $params(-type) == "normal") && \ $log_body != ""} { ::message_archive::log_message \ [connection_jid $xlib] \ $to $log_subject $log_body $xs } return [list success $xs] } proc message::jiddropcmd {target source pos op type data} { set jid [lindex $data 1] $target delete 0 end $target insert 0 $jid } ############################################################################### proc message::show_subscribe_dialog {xlib from type x args} { variable msgid if {$type != "subscribe"} return set status "" foreach {opt val} $args { switch -- $opt { -status { set status $val } default { debugmsg message \ "SHOW_SUBSCRIBE_MESSAGE: unknown option $opt $val" } } } set mw .subscshow[incr msgid] toplevel $mw -class Message wm group $mw . wm withdraw $mw set title [::msgcat::mc "Subscription request from %s" $from] wm title $mw $title wm iconname $mw $title set bbox [ButtonBox $mw.buttons -spacing 0 -padx 10 -default 0] $bbox add -text [::msgcat::mc "Approve subscription"] \ -command [list [namespace current]::subscribe $mw $xlib $from] $bbox add -text [::msgcat::mc "Decline subscription"] \ -command [list [namespace current]::unsubscribe $mw $xlib $from] bind $mw "ButtonBox::invoke [double% $bbox] default" bind $mw "ButtonBox::invoke [double% $bbox] 1" pack $bbox -side bottom -anchor e -padx 2m -pady 2m set sep [Separator::create $mw.sep -orient horizontal] pack $sep -pady 1m -fill x -side bottom frame $mw.frame pack $mw.frame -side top -fill both -expand yes -padx 2m -pady 2m frame $mw.subj pack $mw.subj -side top -anchor w -fill x -expand no -in $mw.frame grid columnconfigure $mw.subj 1 -weight 1 if {[llength [connections]] > 1} { label $mw.subj.lconnection -text [::msgcat::mc "Received by:"] label $mw.subj.connection -text " [connection_jid $xlib]" grid $mw.subj.lconnection -row 0 -column 0 -sticky e grid $mw.subj.connection -row 0 -column 1 -sticky w } label $mw.subj.lab -text [::msgcat::mc "Subscription request from:"] menubutton $mw.subj.mb -text $from -menu $mw.subj.mb.menu menu $mw.subj.mb.menu -tearoff 0 \ -postcommand [namespace code [list subject_menu $mw.subj.mb.menu $xlib $from subscribe]] grid $mw.subj.lab -row 1 -column 0 -sticky e grid $mw.subj.mb -row 1 -column 1 -sticky w foreach tag [bind Menubutton] { if {[string first 1 $tag] >= 0} { regsub -all 1 $tag 3 new bind $mw.subj.mb $new [bind Menubutton $tag] } } frame $mw.space pack $mw.space -side top -fill x -pady 0.5m -in $mw.frame ScrolledWindow $mw.sw pack $mw.sw -side top -fill both -expand yes -in $mw.frame text $mw.body -width 60 -height 8 -wrap word $mw.body insert 0.0 $status $mw.body configure -state disabled $mw.sw setwidget $mw.body BWidget::place $mw 0 0 center wm deiconify $mw } hook::add client_presence_hook message::show_subscribe_dialog ############################################################################### proc message::show_unsubscribed_dialog {xlib from type x args} { variable msgid if {$type != "unsubscribed"} return MessageDlg .unsubscribed[incr msgid] -aspect 50000 -icon info \ -title [::msgcat::mc "Unsubscribed from %s" $from] \ -message [::msgcat::mc "You are unsubscribed from %s" $from] \ -type user -buttons {ok} -default 0 -cancel 0 } hook::add client_presence_hook message::show_unsubscribed_dialog ############################################################################### proc message::subscribe {mw xlib jid} { ::xmpp::sendPresence $xlib -to $jid -type subscribed switch -- [roster::itemconfig $xlib \ [::xmpp::jid::normalize $jid] -subsc] { {} - none - from { message::send_subscribe_dialog $jid -connection $xlib } } destroy $mw } ############################################################################### proc message::unsubscribe {mw xlib jid} { ::roster::remove_item $xlib [::xmpp::jid::normalize $jid] destroy $mw } ############################################################################### proc message::destroy_subscription_dialogs {xlib jid name groups subsc ask} { switch -- $subsc { both - from {} default { return } } foreach mw [winfo children .] { if {![string match .subscshow* $mw]} continue set curjid [$mw.subj.mb cget -text] if {$curjid == $jid} { destroy $mw } } } hook::add roster_push_hook message::destroy_subscription_dialogs ############################################################################### proc message::disco_popup_menu {m bw tnode data parentdata} { lassign $data type xlib jid node switch -- $type { item - item2 { set identities [disco::browser::get_identities $bw $tnode] foreach id $identities { if {[::xmpp::xml::getAttr $id category] == "client"} { subject_menu $m $xlib $jid message return } } } } } hook::add disco_node_menu_hook message::disco_popup_menu ############################################################################### proc message::subject_menu {m xlib jid type} { if {![winfo exists $m]} { menu $m -tearoff 0 } else { $m delete 0 end foreach mm [winfo children $m] { destroy $mm } } hook::run message_dialog_menu_hook $m $xlib $jid return $m } ############################################################################### proc message::add_separator {m xlib jid} { $m add separator } hook::add message_dialog_menu_hook \ [namespace current]::message::add_separator 40 hook::add message_dialog_menu_hook \ [namespace current]::message::add_separator 50 ############################################################################### proc message::add_subscribe_menu_item {m xlib jid} { set chatid [chat::chatid $xlib [::xmpp::jid::stripResource $jid]] if {[chat::is_groupchat $chatid]} { set real_jid [muc::get_real_jid $xlib $jid] if {$real_jid != ""} { set jid $real_jid set state normal } else { set state disabled } } else { set state normal } set user [::xmpp::jid::stripResource $jid] if {[roster::itemconfig $xlib $user -subsc] != ""} { set state disabled } $m add command -label [::msgcat::mc "Add user to roster..."] \ -state $state \ -command [list message::send_subscribe_dialog $user \ -connection $xlib] } hook::add chat_create_user_menu_hook \ [namespace current]::message::add_subscribe_menu_item 35 hook::add message_dialog_menu_hook \ [namespace current]::message::add_subscribe_menu_item 35 hook::add search_popup_menu_hook \ [namespace current]::message::add_subscribe_menu_item 35 ############################################################################### proc message::send_subscribe_dialog {to args} { variable msgid variable send_subscribe_xlib if {[lempty [connections]]} return set cstate normal foreach {opt val} $args { switch -- $opt { -connection { set xlib $val set cstate disabled } } } if {![info exists xlib]} { set xlib [lindex [connections] 0] } set send_subscribe_xlib [connection_jid $xlib] set mw .subscsend[incr msgid] toplevel $mw -class Message wm group $mw . wm withdraw $mw if {[cequal $to ""]} { set title [::msgcat::mc "Send subscription request"] } else { set title [::msgcat::mc "Send subscription request to %s" $to] } wm title $mw $title wm iconname $mw $title set bbox [ButtonBox $mw.buttons -spacing 0 -padx 10 -default 0] $bbox add -text [::msgcat::mc "Request subscription"] \ -command [list message::send_subscribe0 $mw] $bbox add -text [::msgcat::mc "Cancel"] -command [list destroy $mw] bind $mw "ButtonBox::invoke [double% $bbox] default" bind $mw "ButtonBox::invoke [double% $bbox] 1" pack $bbox -side bottom -anchor e -padx 2m -pady 2m set sep [Separator::create $mw.sep -orient horizontal] pack $sep -pady 1m -fill x -side bottom frame $mw.frame pack $mw.frame -side top -fill both -expand yes -padx 2m -pady 2m frame $mw.subj pack $mw.subj -side top -anchor w -fill x -expand no -in $mw.frame grid columnconfigure $mw.subj 1 -weight 1 set connections {} if {[llength [connections]] > 1} { foreach c [connections] { lappend connections [connection_jid $c] } label $mw.subj.lconnection -text [::msgcat::mc "From: "] ComboBox $mw.subj.connection \ -textvariable [namespace current]::send_subscribe_xlib \ -values $connections \ -state $cstate grid $mw.subj.lconnection -row 0 -column 0 -sticky e grid $mw.subj.connection -row 0 -column 1 -sticky ew } label $mw.subj.lab -text [::msgcat::mc "Send request to: "] entry $mw.subj.entry $mw.subj.entry insert 0 $to grid $mw.subj.lab -row 1 -column 0 -sticky e grid $mw.subj.entry -row 1 -column 1 -sticky ew frame $mw.space pack $mw.space -side top -fill x -in $mw.frame -pady 0.5m ScrolledWindow $mw.sw pack $mw.sw -side top -fill both -expand yes -in $mw.frame textUndoable $mw.body -width 60 -height 8 -wrap word $mw.body insert 0.0 [::msgcat::mc "I would like to add you to my roster."] $mw.sw setwidget $mw.body focus $mw.subj.entry BWidget::place $mw 0 0 center wm deiconify $mw } ############################################################################### proc message::send_subscribe0 {mw} { variable send_subscribe_xlib foreach c [connections] { if {[connection_jid $c] == $send_subscribe_xlib} { set xlib $c } } if {[info exists xlib]} { send_subscribe $mw $xlib } } ############################################################################### proc message::send_subscribe {mw xlib args} { set jid [$mw.subj.entry get] ::xmpp::sendPresence $xlib -to $jid -type subscribe \ -status [$mw.body get 1.0 end] ::xmpp::sendIQ $xlib set \ -query [::xmpp::xml::create query \ -xmlns jabber:iq:roster \ -subelement [::xmpp::xml::create item \ -attrs [list jid $jid]]] \ -command "itemedit::show_dialog \ [list $xlib [::xmpp::jid::normalize $jid]] ;#" destroy $mw } ############################################################################### proc message::resubscribe_menu_item {m xlib jid} { set rjid [roster::find_jid $xlib $jid] if {$rjid == ""} { set state disabled } else { set state normal } set mm [menu $m.subscription -tearoff 0] $mm add command -label [::msgcat::mc "Request subscription"] \ -command [list ::xmpp::sendPresence $xlib \ -to $rjid \ -type subscribe] $mm add command -label [::msgcat::mc "Grant subscription"] \ -command [list ::xmpp::sendPresence $xlib \ -to $rjid \ -type subscribed] $mm add command -label [::msgcat::mc "Request unsubscription"] \ -command [list ::xmpp::sendPresence $xlib \ -to $rjid \ -type unsubscribe] $mm add command -label [::msgcat::mc "Revoke subscription"] \ -command [list ::xmpp::sendPresence $xlib \ -to $rjid \ -type unsubscribed] $m add cascade -label [::msgcat::mc "Subscription"] \ -menu $mm \ -state $state } hook::add roster_jid_popup_menu_hook message::resubscribe_menu_item 30 hook::add roster_service_popup_menu_hook message::resubscribe_menu_item 30 ############################################################################### # vim:ts=8:sw=4:sts=4:noet