Updated 2012-10-16 11:34:08 by DDG
 # 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