# snitdnd: implements simple mechanism for drag-and-drop within Tk
# applications. based on [simplednd]
# (c) 2009 WordTech Communications LLC (simplednd). License: standard Tcl
# Kevin Walzer http://www.codebykevin.com/opensource/xplat_oss.html
# (c) 2012 Dr. Detlef Groth, University of Potsdam (snitdnd). License: standard Tcl
# for license see http://www.tcl.tk/software/tcltk/license.html
# starkit-file: https://bitbucket.org/mittelmark/tcl-code/downloads/snitdnd.kit
##############################################################################
# based on simplednd from the Tcl-Wiki by WordTexCommunications LLC
# snitdnd advantages:
# * no collision if several registered drag events have to share the same variable
# * improved demo
# * -trackcmd option can be supplied to monitor the actual object on the target site.
# this is demonstraded by hilighting the dragtargets in the demo
# just do a:
# % package require snitdnd
# % snitdnddemo::demo
# to see the demo example
# you can drag items from the left to the middle listbox
# you can drag items from the middle to the left listbox
# you can drag items within the right listbox
package require snit
snit::type snitdnd {
option -trackcmd {}
option -dragcmd {}
option -dropcmd {}
option -target {}
option -source {}
option -dragtextvar ""
typevariable dragicon
typevariable dragtext
typevariable dragimage
variable targetdirection
constructor {args} {
$self configurelist $args
$self dragRegister $options(-source) $options(-target) \
$options(-dragcmd) $options(-dropcmd) $options(-trackcmd)
trace add variable $options(-dragtextvar) write [mymethod setDragText]
}
method setDragText {varname key op} {
upvar $varname var
set dragtext $var
}
method makeDragIcon {txt img} {
#create the icon
if {![info exists dragicon]} {
set dragicon [toplevel .dnd]
set dragtext $txt
set dragimage $img
wm overrideredirect $dragicon true
label $dragicon.view -image $dragimage -text $dragtext -compound left
pack $dragicon.view
#now hide the icon
wm withdraw $dragicon
}
}
# register widget to respond to drag events: widget to register, its
# target widget, callback to associate with this drag event, text for the
# drag label, and image for the drag label
method dragRegister {w target dragcmd dropcmd trackcmd} {
puts "cmd: $options(-trackcmd)"
$self makeDragIcon {} {}
puts "$w registered as dragsite with $target as the drop target"
#binding for when drag motion begins
bind $w <B1-Motion> [mymethod dragMove %W %X %Y $dragcmd $target]
#binding for when drop event occurs
bind $w <ButtonRelease-1> [mymethod dragStop %W %X %Y $target $dropcmd]
}
# drag motion with following args: source widget, cursor x position,
# cursor y position, drag command, target widget
method dragMove {w x y dragcmd target} {
#the dragcmd properly configures the drag icon
eval $dragcmd
#configure drag icon with customized text and image
$dragicon.view configure -text $dragtext -image $dragimage
#dragicon appears
wm deiconify $dragicon
catch {raise $dragicon}
#this places the drag icon below the cursor
set x [expr {$x - ([winfo reqwidth $dragicon] / 2) }]
set y [expr {$y - [winfo reqheight $dragicon] + 25 }]
wm geometry $dragicon +$x+$y
$self trackCursor $w $x $y $target
}
# track the cursor, change if it is over the drop target; args are source
# widget (w), x pos (x), y pos (y), target widget (target)
method trackCursor {w x y target} {
#get the coordinates of the drop target
set targetx [winfo rootx $target]
set targety [winfo rooty $target]
set targetwidth [expr [winfo width $target] + $targetx]
set targetheight [expr [winfo height $target] + $targety]
if {$options(-trackcmd) ne ""} {
$options(-trackcmd) $w $x $y $target
}
#change the icon if over the drop target
if {($x > $targetx) && ($x < $targetwidth) && ($y > $targety) && ($y < $targetheight)} {
$w configure -cursor based_arrow_up
} else {
$w configure -cursor dot
}
}
# dragstop/drop event with following args: source widget, cursor x
# position, cursor y position, target widget, dropcommand: if over drop
# target, execute dropcommand; otherwise simply return
method dragStop {w x y target dropcmd} {
#hide dragicon on drop event
wm withdraw $dragicon
#change cursor back to arrow
$w configure -cursor arrow
#execute callback or simply return
if {[winfo containing $x $y] != $target} {
puts "target $w not reached"
} else {
focus -force $target
eval $dropcmd
}
}
}
namespace eval snitdnddemo {
#demo package
proc demo {} {
variable dragicon
variable dragtext
variable dragimage
#create image for demo
image create photo dnd_demo -data {R0lGODlhEAAQALMAAAAAAMbGxv//////////////////////////////////\
/////////////////////yH5BAEAAAEALAAAAAAQABAAAAQwMMhJ6wQ4YyuB\
+OBmeeDnAWNpZhWpmu0bxrKAUu57X7VNy7tOLxjIqYiapIjDbDYjADs=}
listbox .l -selectmode single -activestyle none
listbox .b -selectmode single -activestyle none
listbox .s -selectmode single -activestyle none
foreach item {do re mi} {
.l insert end $item
.s insert end $item
}
foreach item {fa so la} {
.b insert end $item
.s insert end $item
}
pack .l -side left
pack .b -side left
pack .s -side left
#register drag sources, drag targets, and callbacks
snitdnd snd1 -source .l -target .b -dragcmd [namespace current]::drag_l \
-dropcmd [namespace current]::drop_l -trackcmd [namespace current]::track \
-dragtextvar [namespace current]::dragtext
snitdnd snd2 -source .b -target .l -dragcmd [namespace current]::drag_b \
-dropcmd [namespace current]::drop_b -trackcmd [namespace current]::track \
-dragtextvar [namespace current]::dragtext
snitdnd snd3 -source .s -target .s -dragcmd [namespace current]::drag_s \
-dropcmd [namespace current]::drop_s -trackcmd [namespace current]::track \
-dragtextvar [namespace current]::dragtext
}
proc drag_l {} {
variable dragtext
variable dragimage
set item [lindex [.l get [.l curselection]]]
set dragtext $item
set dragimage dnd_demo
}
#dropcommand for demo l widget: callback to execute on drop
proc drop_l {} {
variable dragtext
variable dragimage
variable lastIdx
.b insert $lastIdx(.b) $dragtext
.l delete [.l curselection]
}
#dragcommand for demo b widget: configures dragicon
proc drag_b {} {
variable dragtext
variable dragimage
set item [lindex [.b get [.b curselection]]]
set dragtext $item
set dragimage dnd_demo
}
#dropcommand for demo b widget: callback to execute on drop
proc drop_b {} {
variable dragtext
variable dragimage
variable lastIdx
.l insert $lastIdx(.l) $dragtext
.b delete [.b curselection]
}
proc drag_s {} {
variable dragtext
variable dragimage
set item [lindex [.s get [.s curselection]]]
set dragtext $item
set dragimage navup22 ;#dnd_demo
}
proc drop_s {} {
variable dragtext
variable dragimage
variable lastIdx
.s insert $lastIdx(.s) $dragtext
.s delete [.s curselection]
}
proc track {w x y target} {
variable lastIdx
set idx [$target nearest [expr $y - [winfo rooty $target]]]
puts "y=[expr [winfo rooty $target] + $y]"
#puts [.tf.tbl rowcget $idx -text]
if {[info exists lastIdx($target)]} {
$target itemconfigure $lastIdx($target) -foreground black
$target itemconfigure $lastIdx($target) -background white
}
$target itemconfigure $idx -foreground blue
$target itemconfigure $idx -background "light blue"
set lastIdx($target) $idx
}
}
package provide snitdnd 0.1