#!../tree_wish -f
#
# -*-Tcl-*-
# dragdroptree - itcl demo class to a display tree with drag&drop
# for moving nodes and leaves.  A test directory is used as a basis
# for the tree structure.
#
# Author: Allan Brighton (allan@piano.sta.sub.org) 
#

lappend auto_path ../library 
source ../library/compat.tcl
set_app_defaults


itcl_class Dragdroptree {
    inherit TopLevelWidget
    

    # do the window layout

    method create_main_window {} {
	make_menubar
	pack [frame $this.t] -side top -fill both -expand 1
	make_tree_window $this.t
	make_leaves_window $this.t
	setup_drag&drop
    }

    
    method make_menubar {} {
	add_menubar
   
	# file menu
	set m [add_menubutton File]
	$m add command -label "Print Tree..." \
		-command "$this print_tree"
	$m add separator
	$m add command -label "Exit" \
		-command "$this quit"

	# View menu
	set m [add_menubutton View]
	$m config -postcommand "$this set_view_menu $m"
	$m add command -label "Toggle Tree Layout" \
		-command "$this toggle_tree_layout"
	$m add command -label "Hide Subtree" \
		-command "$this hide_subtree"
	$m add command -label "Show Subtree" \
		-command "$this show_subtree"

	# edit menu
	set m [add_menubutton Edit]
	$m config -postcommand "$this set_edit_menu $m"
	$m add command -label "Insert New Node" \
		-command "$this insert_node"
	$m add command -label "Remove Selected Node" \
		-command "$this remove_node"
	$m add command -label "Remove Selected Leaves" \
		-command "$this remove_leaves"

	# help menu
	set m [add_menubutton Help right]
	$m add command -label "Help..." \
		-command ""
    }


    # Display a print dialog to print the tree as postscript

    method print_tree {} {
	utilReUseWidget CanvasPrint $this.printtree -canvas $canvas
    }


    # make "Edit" menu items sensitive or not depending on the current
    # selection

    method set_edit_menu {m} {
	if {![lempty [$this.leaves.listbox curselection]]} {
	    $m entryconfig "Remove Selected Leaves" \
		    -state normal
	} else {
	    $m entryconfig "Remove Selected Leaves" \
		    -state disabled
	}

	if {[lempty [$tree get_selected]]} {
	    $m entryconfig "Insert New Node" \
		    -state disabled
	    $m entryconfig "Remove Selected Node" \
		    -state disabled
	} else {
	    $m entryconfig "Insert New Node" \
		    -state normal
	    $m entryconfig "Remove Selected Node" \
		    -state normal
	}
    }
    

    # make "View" menu items sensitive or not depending on the current
    # selection

    method set_view_menu {m} {
	if {[lempty [$tree get_selected]]} {
	    $m entryconfig "Hide Subtree" \
		    -state disabled
	    $m entryconfig "Show Subtree" \
		    -state disabled
	} else {
	    $m entryconfig "Hide Subtree" \
		    -state normal
	    $m entryconfig "Show Subtree" \
		    -state normal
	}
    }
    

    # make the tree window to display the contents
    # of selected nodes

    method make_tree_window {frame} {
	# add a frame for the title and tree
	pack [set f [frame $this.treef]] \
		-side left -fill both -expand 1 -in $frame
	pack [label $f.title -text "Nodes"] -side top
		
	set tree [Tree $this.tree \
		-parentdistance 80 \
		-borderwidth 30 \
		-layout vertical \
		-bitmap_color red]
	pack $tree -side top -fill both -expand 1 -in $f

	# save internal tree widget name for faster access to some ops
	set canvas [$tree info protected canvas -value]
	
	# add binding to display info when node is selected
	$canvas bind text <1> "+$this list_leaves"
	$canvas bind bitmap <1> "+$this list_leaves"
    }


    # make the listbox for displaying the leaves of the selected node
    
    method make_leaves_window {frame} {
	pack [Listbox $this.leaves -title Leaves \
		-hscroll 1 -single_select 0] \
		-side left -fill both -expand 1 -in $frame
    }


    # set up the drag&drop operations for moving nodes and leaves

    method setup_drag&drop {} {
	if {[llength [::info commands blt_*]] == 0} {
	    return
	}
	
	blt_drag&drop source $canvas config \
		-packagecmd "$this package_node" \
		-selftarget 1

	blt_drag&drop source $this.leaves.listbox config \
		-packagecmd "$this package_leaf"

	blt_drag&drop source $canvas  handler \
		node "$this send_node" \
		leaf "$this send_leaf"

	blt_drag&drop source $this.leaves.listbox  handler \
		leaf "$this send_leaf"
		
	blt_drag&drop target $canvas handler \
		node "$this drop_node" \
		leaf "$this drop_leaf"

	blt_drag&drop errors {error}
    }


    # drag&drop package command for tree nodes

    method package_node {win} {
	if {[winfo children $win] == ""} {
	    label $win.value
	    pack $win.value
	}
	set node [$tree get_selected]
	$win.value config -bitmap @$node_bitmap
	return $node
    }


    # drag&drop package command for leaves

    method package_leaf {win} {

	set indexes [$this.leaves.listbox curselection]
	set n [llength $indexes]
	if {$n == 0} {
	    error "first make a selection in the list"
	} 
	if {[winfo children $win] == ""} {
	    label $win.value
	    pack $win.value
	}
	if {$n == 1} {
	    $win.value config -bitmap @$leaf_bitmap
	} else {
	    $win.value config -bitmap @$leaves_bitmap
	}
	return $indexes
    }


    # drag&drop target command for nodes

    method drop_node {args} {
	set target [$tree get_current]
	# since we are using the path name as the tag, we have
	# to rm the node and add a new one with the new tag
	set label [file tail $ddnode]
	set newname $target/$label
	exec mv $ddnode $newname
	$tree rmlink $ddnode
	$tree add_node $target $newname \
		-bitmap @$node_bitmap \
		-label $label
	$tree draw
    }


    # drag&drop target command for leaves.
    # (Note: for some reason, we can't get the current canvas 
    # item until we return to the event loop...)

    method drop_leaf {args} {
	after 0 $this drop_leaf2
    }
    method drop_leaf2 {} {
	set target [$tree get_current]
	foreach leaf [$this.leaves get_selected] {
	    exec mv $cur_node/$leaf $target
	}
	$this.leaves remove_selected
    }

    
    # drag&drop send command for nodes

    method send_node {interp ddwin data} {
	set ddnode $data
	blt_drag&drop target $ddwin handle node
    }


    # drag&drop send command for leaves

    method send_leaf {interp ddwin args} {
	blt_drag&drop target $ddwin handle leaf
    }


    # toggle between vertical and horizontal layout
    
    method toggle_tree_layout {} {
	busy {
	    $tree toggle_layout
	}
    }


    # expand the tree to include all posible nodes 
    # under the given parent node, or root node

    method expand_tree {parent} {
	busy {
	    expand_tree_ $parent
	}
	$tree draw
    }


    # recursively add all subnodes of the given node (internal version)

    method expand_tree_ {parent} {
	foreach node [$this.dir lsdirs $parent] {
	    $tree add_node $parent $parent/$node \
		    -bitmap @$node_bitmap \
		    -label $node
	    expand_tree_ $parent/$node
	}
    }


    # list the leaves in the current node

    method list_leaves {} {
	busy {
	    set cur_node [$tree get_selected]
	    clear
	    foreach leaf [$this.dir lsfiles $cur_node] {
		$this.leaves append $leaf
	    }
	}
    }

    
    # hide the selected subtree

    method hide_subtree {} {
	if {"[set node [get_node]]" == ""} {
	    return
	}
	$tree prune $node
	$tree draw
    }

     
    # add the subnodes of the selected node

    method show_subtree {} {
	if {"[set node [get_node]]" == ""} {
	    return
	}
	expand_tree $node
    }
   

    # insert a new node in the tree

    method insert_node {} {
	if {"[set parent [get_node]]" == ""} {
	    return
	}
	set label [input_dialog "Please enter the name of the new node:"]
	set node $parent/$label
	exec mkdir $node

	$tree add_node $parent $node \
		-bitmap @$node_bitmap \
		-label $label
	$tree draw
    }

    
    # remove the selected node from the tree

    method remove_node {} {
	if {"[set node [get_node]]" == ""} {
	    return
	}
	exec rmdir $node
	$tree rmlink $node
	$tree draw
	clear
    }

    
    # clear the lists and tables

    method clear {} {
	$this.leaves clear
    }

    
    # remove the selected leaves from the current node

    method remove_leaves {} {
	set list [$this.leaves get_selected]
	if {[lempty $list]} {
	    error_dialog "Please select one or more items from the list"
	    return
	}
	foreach i $list {
	    exec rm -f $cur_node/$leaf
	}
	list_leaves
    }
    

    # get the selected node and make an error message if there isn't one

    method get_node {} {
	set node [$tree get_selected]
	if {"$node" == ""} {
	    error_dialog "Please select a node from the tree"
	}
	return $node
    }


    # create an object of this class

    constructor {config} {
	TopLevelWidget::constructor

	# create a Dir object to list files and directories
	Dir $this.dir

	create_main_window
	wm minsize $this 10 10 
	wm geometry $this 48x20

	set rootnode [pwd]/rootnode
	$tree add_node "" $rootnode \
		-bitmap @$node_bitmap \
		-label rotnode
	expand_tree $rootnode
    }

    
    # destructor: clean up any non-widget itcl objects
    # (the widgets get it automatically)
    
    destructor {
	$this.dir delete
    }


    # -- public members --

    # bitmap to use for nodes
    public node_bitmap "bitmaps/dir.xbm"

    # bitmap to use for a leaf
    public leaf_bitmap  "bitmaps/file.xbm"

    # bitmap to use to represent multiple leaves (drag&drop)
    public leaves_bitmap  "bitmaps/files.xbm"


    # -- protected member variables --


    # itcl tree mega-widget
    protected tree

    # canvas for tree
    protected canvas

    # currently selected tree node
    protected cur_node {}

    # drag&drop vars
    protected ddnode {}
}


# start the application here
Dragdroptree :: start
