Index: RosterTree.tcl =================================================================== --- RosterTree.tcl (revision 2783) +++ RosterTree.tcl (working copy) @@ -39,6 +39,7 @@ namespace eval ::RosterTree { + ::hooks::register initHook ::RosterTree::InitHook ::hooks::register logoutHook ::RosterTree::LogoutHook ::hooks::register quitAppHook ::RosterTree::QuitHook ::hooks::register menuJMainFilePostHook ::RosterTree::FileMenuPostHook @@ -565,7 +566,20 @@ focus [winfo toplevel $win] } +proc ::RosterTree::DnDCopyOrMove { action } { + global DnDAction + set DnDAction $action +} + + +# copy or move a contacts between roster groups +# a contact can be a member of multiple groups proc ::RosterTree::NotifyDragReceive {T dragged target} { + global wDlgs + variable popMenuDefs + global DnDAction + + set DnDAction "not initialized" set jlib [::Jabber::GetJlib] @@ -573,7 +587,7 @@ if {[$T item id $target] eq ""} { return } - + # Find target group or empty. set tag [GetTagOfItem $target] set tag0 [lindex $tag 0] @@ -582,36 +596,93 @@ set ptag [GetTagOfItem $parent] set ptag0 [lindex $ptag 0] if {$ptag0 eq "group"} { - set groups [list [lindex $ptag 1]] + set tgroup [list [lindex $ptag 1]] } elseif {$ptag0 eq "head"} { - set groups "" + set tgroup "" } else { return } } elseif {$tag0 eq "group"} { - set groups [list [lindex $tag 1]] + set tgroup [list [lindex $tag 1]] } elseif {$tag0 eq "head"} { - set groups "" + set tgroup "" } else { return } + # due to the fact that a contact can belong to + # multiple groups, we need to ask the user whether + # he wants to copy or move the contact to the new group + # using a small popup menu + set m $wDlgs(jpopuproster) + destroy $m + menu $m -tearoff 0 + set mDef $popMenuDefs(rostertree,dnd,def) + set mType $popMenuDefs(rostertree,dnd,type) + + ::AMenu::Build $m $mDef + # put the popup menu below the mouse pointer + set X [winfo pointerx $T] + set Y [winfo pointery $T] + tk_popup $m [expr int($X) - 10] [expr int($Y) - 10] + # the popup needs to be done synchronous, therefore + # we have to wait for the variable change + # the popup menu is not modal, therefore it might disappear + # before the user clicks into it, to not wait forever, define a timeout + after 30000 { set DnDAction "cancel" } + tkwait variable DnDAction + if {[winfo exists $m]} { + destroy $m + } + # check if the user wants to cancel the operation, or whether the + # menu disappeared and DnDAction is still not initialized + if { $DnDAction eq "cancel" || $DnDAction eq "not initialized" } { + return + } foreach item $dragged { + set jid "" + set origgroups "" + set sparent "" + set sptag "" + set tag "" if {[$T item id $item] eq ""} { continue } - set tag [GetTagOfItem $item] - if {[lindex $tag 0] eq "jid"} { + set tag [GetTagOfItem $item] + if {[lindex $tag 0] eq "jid" } { set jid [lindex $tag 1] set jid [$jlib roster getrosterjid $jid] + # because the user can be a member of multiple groups, we need to figure + # out to which groups the user actually belongs to + set origgroups [::Jabber::Jlib roster getgroups $jid] + if { $DnDAction eq "copy" } { + set groups [lsearch -all -inline -not -exact $origgroups $tgroup] + lappend groups $tgroup + lsort -unique $groups + } else { + # when we move the contact, we also need to know the group from which + # the contact needs to be removed + set sparent [$T item parent $item] + set sptag [GetTagOfItem $sparent] + if {[lindex $sptag 0] eq "group" } { + set sgroup [lindex $sptag 1] + set groups [lsearch -all -inline -not -exact $origgroups $sgroup] + lappend groups $tgroup + lsort -unique $groups + } elseif {[lindex $sptag 0] eq "head" } { + set groups [lappend origgroups $tgroup] + lsort -unique $groups + } + } array unset rostA set rostA(-groups) [list] array set rostA [$jlib roster getrosteritem $jid] if {$rostA(-groups) ne $groups} { - unset -nocomplain rostA(-subscription) - set rostA(-groups) $groups - eval {$jlib roster send_set $jid} [array get rostA] + unset -nocomplain rostA(-subscription) + set rostA(-groups) $groups + eval {$jlib roster send_set $jid} [array get rostA] } + } } } @@ -1761,6 +1832,29 @@ } } +proc ::RosterTree::InitHook {} { + InitMenus +} + +proc ::RosterTree::InitMenus {} { + # Template for the DnD popup menu. + variable popMenuDefs + global DnDAction +::Debug 2 "::RosterTree::InitMenus YEAAAAHHHHHHHHHHHHH" + set mDefs { + {command mCopy {[mc "copy"]} {::RosterTree::DnDCopyOrMove "copy"}} + {command mMove {[mc "move"]} {::RosterTree::DnDCopyOrMove "move"}} + {command mCancel {[mc "cancel"]} {::RosterTree::DnDCopyOrMove "cancel"}} + } + set mTypes { + {mCopy {normal} } + {mMove {normal} } + {mCancel {normal} } + } + set popMenuDefs(rostertree,dnd,def) $mDefs + set popMenuDefs(rostertree,dnd,type) $mTypes +} + proc ::RosterTree::LogoutHook {} { variable balloon