#
# editbar.tcl: routines for SHADOW beamline control toolbar
#
# ------------------------------------------------
# Mumit Khan <khan@xraylith.wisc.edu>
# Center for X-ray Lithography
# University of Wisconsin-Madison
# 3731 Schneider Dr., Stoughton, WI, 53589
# ------------------------------------------------
#
# Copyright (c) 1994-1996 Mumit Khan
#
#


#
# set some local private globals used in the Editbar. The inspectors
# are added with a different style (eg., a different font).
#
set editbar_priv(regular_style)		""
set editbar_priv(editing_style)		""
set editbar_priv(inspector_style)	""
set editbar_priv(enabled_style)		""
set editbar_priv(disabled_style)	""

#
# editbar:make: make a icon/item edit bar/box. This works in double-click
# mode, so that a single click selects it and a double click executes it,
# ie., calls the command callback.
#
# w: Enclosing frame. Already created.
# toolbox: The initial toolbox elements. Each list item describes a
#     Toolbox item, and is of the following format:
#	    {Path Header/Entry enabled/disabled {callback_data}}
#     eg.,
#	    {System header disabled ""}
#	    {System.Source entry enabled ""}
#
# callback: The command callback routine (with either a double-click or 
#    <return>).
#
# select_callback: Called when an item is being browsed.
#
# delete_callback: Called when an item is being deleted. FIXME/CHECK.
#
proc editbar:make {w {toolbox {}} {callback ""} {select_callback ""} \
    {delete_callback ""}} {
    global editbar_priv

    #
    # We create the frame and the ScrolledHList widget
    # at the top of the dialog box
    #
    frame $w.toolbox

    set tree $w.toolbox.a
    #
    # we used to have single mode attribute, but not anymore.
    #
    # hlist.selectMode single
    #
    tixTree $tree -options {
	hlist.separator "."
	hlist.header true
	relief flat
	takefocus 1
    }
    pack $tree -expand yes -fill both -side left 

    set hlist [$tree subwidget hlist]
    $tree config  \
        -browsecmd "editbar:_browse $hlist [list $select_callback]"  \
	-command "editbar:_select $hlist [list $callback]"
    
    #
    # Add the tools to the hlist.
    #

    # First some styles for the headers
    set style(header) [tixDisplayStyle text  -refwindow $hlist \
        -fg black -anchor c \
        -padx 8 -pady 5 \
        -font [tix option get bold_font ]
    ]

    $hlist header create 0 -itemtype text \
	-text "System" -style $style(header)

    foreach t $toolbox {
        set tool [lindex $t 0]
	set type [lindex $t 1]
	set state [lindex $t 2]
	set data "[lindex $t 3] 0"
	if {$state == "enabled"} {
	    set image [tix getimage file]
	} else {
	    set image [tix getimage folder]
	}
	set text [lindex [split $tool .] end]
	$hlist add $tool -itemtype imagetext \
	    -text $text -image $image \
	    -state $state -data $data
    }

    #
    # DO NOT SET "autosetmode"! Otherwise the idea of opening/closing
    # can confuse issues. Better left completely opened.
    #
    # $tree autosetmode

    #
    # make the various Display Styles that we use here.
    #
    set editbar_priv(inspector_style) [tixDisplayStyle imagetext \
	-refwindow $hlist -fg magenta]

    set editbar_priv(editing_style) [tixDisplayStyle imagetext \
	-refwindow $hlist -fg SteelBlue]

    set editbar_priv(regular_style) [tixDisplayStyle imagetext \
	-refwindow $hlist -fg black]

    set editbar_priv(disabled_style) [tixDisplayStyle imagetext \
	-refwindow $hlist -fg grey]

    set editbar_priv(enabled_style) [tixDisplayStyle imagetext \
	-refwindow $hlist -fg black]
    
    if {$delete_callback != ""} {
	bind $hlist <Delete> $delete_callback
    }

    return $w.toolbox
}

proc editbar:_toolname_to_path {item {sep .}} {
    global editbar_priv

    set itype [string tolower [lindex $item 0]]
    switch -glob -- $itype {
	"source" {
	    set path System.Source
	}
	"oe" {
	    set oe_id [lindex $item 1]
	    set path System.OE$oe_id
	}
	"scr*" {
	    set oe_id [lindex $item 1]
	    set scr_id [lindex $item 2]
	    set path System.OE$oe_id.Screen$scr_id
	}
	"inspector" {
	    set path System.
	    set id 0
	    set object [string tolower [lindex $item 1]]
	    switch -glob -- $object {
		"source" {
		    set id [lindex $item 2]
		    append path Source.
		} 
		"oe" {
		    set oe [lindex $item 2]
		    set id [lindex $item 3]
		    append path OE$oe.
		}
		"scr*" {
		    set oe [lindex $item 2]
		    set scr [lindex $item 3]
		    set id [lindex $item 4]
		    append path OE$oe.Screen$scr.
		}
		default {
		    error "editbar:_toolname_to_path: WRONG ITEM \"$item\""
		}
	    }
	    append path Inspector$id
	}
	default {
	    error "editbar:_toolname_to_path: WRONG ITEM \"$item\""
	    set path ""
	}
    }
    return $path
}

#
# EDITBAR_ADD: add an icon/item to toolbar
#
# if icon is not supplied, the names are displayed instead.
#    SOURCE ==> SOURCE
#    OE     ==> OE 1
#    SCR    ==> SCR1(1)
#
# type is either "header" or "entry". Ignored for now.
#
# args:
#    {SOURCE <title>}
#    {OE oe_id <title>}
#    {SCR oe_id scr_id <title>}
#    {INSPECTOR BEAMLINE <name> id <title>}
#    {INSPECTOR SOURCE <name> id <title>}
#    {INSPECTOR OE oe_id <name> id <title>}
#    {INSPECTOR SCR oe_id scr_id <name> id <title>}
#
# The item_info is stored inside the node as "data" in addition to the
# the modified flag, which is always the last element in the "data"
# list.
#
proc editbar:add {eb_w type item_info} {
    global editbar_priv

    set hlist [$eb_w subwidget hlist]
    set what [string tolower [string trim [lindex $item_info 0]]]
    #
    # LABELS aren't supported yet. Ignored.
    #
    # set label [lindex $item_info end]
    set label ""
    set extra_config ""
    switch -glob -- $what {
    "source" {
	set path [editbar:_toolname_to_path [list source]]
	if {$label == ""} {set label Source}
	set position 0
	set data [list source 0]
    }
    "oe" {
	set oe_id [lindex $item_info 1]
	set path [editbar:_toolname_to_path [list oe $oe_id]]
	if {$label == ""} {set label "OE $oe_id"}
	set position [expr $oe_id - 1]
	if [$hlist info exists System.Source] {incr position}
	set data [list oe $oe_id 0]
    }
    "scr*" {
	set oe_id [lindex $item_info 1]
	set scr_id [lindex $item_info 2]
	set path [editbar:_toolname_to_path [list screen $oe_id $scr_id]]
	if {$label == ""} {set label "Screen $scr_id"}
	set position [expr $scr_id - 1]
	set data [list screen $oe_id $scr_id 0]
    }
    "inspector" {
	set target_type [string tolower [lindex $item_info 1]]
	set extra_config "-style $editbar_priv(inspector_style)"

	vputs "editbar:add: inspector title = $label"

	switch -glob $target_type {
	"beamline" {
	    dialog .editbar_dlg "Add Editbar Item" \
		"Internal Error: Bad Editbar item name \"$what\"." \
		error 0 Dismiss
	    return ""
	}
	"source" {
	    set inspector [lindex $item_info 2]
	    set id [lindex $item_info 3]

	    set path [editbar:_toolname_to_path [list source]]
	    set num_entries [llength [$hlist info children $path]]
	    append path ".Inspector$id"
	    if {$label == ""} {set label "$inspector $id"}
	    set position $num_entries 
	    set data "[string tolower $item_info] 0"
	}
	"oe" {
	    set oe_id [lindex $item_info 2]
	    set inspector [lindex $item_info 3]
	    set id [lindex $item_info 4]

	    set path [editbar:_toolname_to_path [list oe $oe_id]]
	    set num_entries [llength [$hlist info children $path]]
	    append path ".Inspector$id"
	    if {$label == ""} {set label "$inspector $id"}
	    set position $num_entries
	    set data "[string tolower $item_info] 0"
	}
	"scr*" {
	    set oe_id [lindex $item_info 2]
	    set scr_id [lindex $item_info 3]
	    set inspector [lindex $item_info 4]
	    set id [lindex $item_info 5]

	    set path [editbar:_toolname_to_path [list screen $oe_id $scr_id]]
	    set num_entries [llength [$hlist info children $path]]
	    append path ".Inspector$id"
	    if {$label == ""} {set label "$inspector $id"}
	    set position $num_entries
	    set data "[string tolower $item_info] 0"
	} 
	default {
	    dialog .editbar_dlg "Add Editbar Item" \
		"Internal Error: Bad Editbar item name \"$what\"." \
		error 0 Dismiss
	    return ""
	}
	}
    }
    default {
	dialog .editbar_dlg "Add Editbar Item" \
	    "Internal Error: Bad Editbar item name \"$what\"." \
	    error 0 Dismiss
	return ""
    }
    }
    set image [tix getimage file]
    eval $hlist add $path \
	-at $position \
	-itemtype imagetext \
	-data [list $data] \
	-text [list $label] -image $image \
	$extra_config
    vputs "editbar:add: path = $path"
    editbar:_highlight $hlist $path
}

#
# EDITBAR_DELETE: remove an icon/item from toolbar
#
proc editbar:delete {eb_w item_info} {
    set hlist [$eb_w subwidget hlist]
    set type [string trim [lindex $item_info 0]]
    set itype [string tolower $type]
    set path ""
    switch -glob -- $itype {
	"source" {
	    set path [editbar:_toolname_to_path [list source]]
	}
	"oe" {
	    set oe_id [lindex $item_info 1]
	    set path [editbar:_toolname_to_path [list oe $oe_id]]
	}
	"scr*" {
	    set oe_id [lindex $item_info 1]
	    set scr_id [lindex $item_info 2]
	    set path [editbar:_toolname_to_path [list screen $oe_id $scr_id]]
	}
	inspector {
	    set path [editbar:_toolname_to_path $item_info]
	}
	default {
	    dialog .editbar_dlg "Add Editbar Item" \
		"Internal Error: Bad Editbar item name \"$type\"." \
		error 0 Dismiss
	    return ""
	}
    }
    $hlist delete entry $path
}

#
# EDITBAR_DELETE_ALL_SCR: removes all SCRs of an OE
#
proc editbar:delete_all_scr {eb_w oe_id num_scr} {
    set hlist [$eb_w subwidget hlist]
    set path [editbar:_toolname_to_path [list oe $oe_id]]
    #
    # can't simply delete all the offsprings, since we may also have
    # inspection tools intermixed with SCREENs in the OE.
    #
    # $hlist delete offsprings $path
    #
    for {set scr 1} {$scr <= $num_scr} {incr scr} {
	editbar:delete [list scr $oe_id $scr]
    }
}

#
# FIXME/TODO: This is not correct. When we add SYSTEM inspectors, this is
# going to break horrendously. Should use "editbar:delete" for each OE
# to fix it.
#
proc editbar:delete_all_oe {eb_w num_oe} {
    set hlist [$eb_w subwidget hlist]
    if ![$hlist info exists System.Source] {
        $hlist delete offsprings System
    } else {
        $hlist delete siblings System.Source
    }
    return $num_oe
}

proc editbar:_path_to_toolname {path {sep .}} {
    return [lindex [split $path $sep] end]
}

proc editbar:_select {hlist callback path} {
    set toolname [editbar:_path_to_toolname $path]
    if {$callback != ""} {
        vputs "editbar:_select: eval $callback [$hlist info data $path]"
        eval $callback [$hlist info data $path]
    }
}

proc editbar:_browse {hlist callback path} {
    set toolname [editbar:_path_to_toolname $path]
    if {$callback != ""} {
        vputs "editbar:_browse: eval $callback [$hlist info data $path]"
        eval $callback [$hlist info data $path]
    }
}

proc editbar:_highlight {hlist path} {
    $hlist selection clear
    $hlist anchor clear
    $hlist anchor set $path
    $hlist selection set $path
    $hlist see $path
}

proc editbar:highlight {w item} {
    set hlist [$w subwidget hlist]
    $hlist selection clear
    $hlist anchor clear
    if {$item != ""} {
	set path [editbar:_toolname_to_path $item]
	$hlist anchor set $path
	$hlist selection set $path
	$hlist see $path
    }
}

proc editbar:edit_highlight {w item being_edited} {
    return
    #
    # not used yet.
    #
    global editbar_priv
    set hlist [$w subwidget hlist]
    set path [editbar:_toolname_to_path $item]
    if $being_edited {
	set style $editbar_priv(editing_style)
    } else  {
	set style $editbar_priv(regular_style)
    }
    $hlist entryconfigure $path -style $style
}

proc editbar:_match_property {info property} {
    set info_len [llength $info]
    set prop_len [llength $property]
    if {$prop_len > $info_len} {	;# all bets are off.
	return 0
    }
    foreach prop in $property {
	if {[lsearch -exact $info $prop] == -1} {
	    return 0
	}
    }
    return 1
}

#
# returns the position of the node with the given properties. The property
# list has to match completely. Returns 0..n-1 if found, or -1 if not.
#
proc editbar:_find_node_position {hlist toplevel property} {
    set i 0
    set found 0
    foreach $item $toplevel {
	set info [$hlist info data $item] 
	if [editbar:_match_property $info $property] {
	    set found 1
	    break
	}
	incr i
    }
    if $found {return $i} {return -1}
}


#
# returns the position of the source in the top-level. Returns 0..n-1 if
# found, or -1 if not.
#
proc editbar:find_source_position {w} {
    set hlist [$w subwidget hlist]
    set toplevel [$hlist info children]
    return [editbar:_find_node_position $hlist $toplevel "source"]
}


proc editbar:find_oe_position {w oe} {
    set hlist [$w subwidget hlist]
    set toplevel [$hlist info children]
    return [editbar:_find_node_position $hlist $toplevel "oe $oe"]
}

proc editbar:find_scr_position {w oe scr} {
    set oe_position [editbar:find_oe_position $w $oe]
    set pos -1
    if {$oe_position != -1} {
	set hlist [$w subwidget hlist]
	set toplevel [editbar:_toolname_to_path [list oe $oe]]
	set pos [editbar:_find_node_position $hlist $toplevel "scr $oe $scr"]
    }
    return $pos
}

#
# args: [list source <id>]
#       [list oe <oe> <id>]
#       [list scr <oe> <scr> <id>]
#
# FIXME/TODO: beamline inspectors not supported yet.
#
proc editbar:find_inspector_position {w args} {
    set nargs [llength $args]
    if {$nargs < 2 || $nargs > 4} {
	return -1
    }

    set hlist [$w subwidget hlist]
    set toplevel ""
    set search_info ""

    set type [string tolower [lindex $args 0]]
    switch -exact $type {
	source {
	    set id [lindex $args 1]
	    set search_info "inspector source $id"
	    set toplevel [editbar:_toolname_to_path [list source]]
	} 
	oe {
	    set oe [lindex $args 1]
	    set id [lindex $args 2]
	    set search_info "inspector oe $oe $id"
	    set toplevel [editbar:_toolname_to_path [list oe $oe]]
	}
	scr {
	    set oe [lindex $args 1]
	    set scr [lindex $args 2]
	    set id [lindex $args 3]
	    set search_info "inspector scr $oe $scr $id"
	    set toplevel [editbar:_toolname_to_path [list scr $oe $scr]]
	}
	default {
	    return -1
	}
    }
    return [editbar:_find_node_position $hlist $toplevel $search_info]
}

proc editbar:selected {w} {
    set hlist [$w subwidget hlist]
    set path_selected [lindex [$hlist info selection] 0]
    vputs "editbar:selected: path = $path_selected"
    set selection_info ""
    if {$path_selected != ""} {
	set selection_info "[$hlist info data $path_selected]"
    }
    vputs "editbar:selected: selection info =  $selection_info"
    return $selection_info
}


#
# TODO/FIXME: Not completed yet. Idea is to redraw the nodes that have
# modified since the last "Run" command, so the user gets feedback on what
# to re-run in the SYSTEM.
#
proc editbar:_draw_modified {hlist path modified} {
    set data [$hlist info data $path]
    set modified_flag [lindex $data end]
    # 
    # redraw if "modified exclusive_OR modified_flag"
    #
    if {($modified && !$modified_flag) || (!$modified && $modified_flag)} {
	set do_redraw 1
    } else {
	set do_redraw 0
    }
    if {$do_redraw} {
	global editbar_priv
    }
}

