#
# page.tcl: create/display/movement/update routines for menu pages
#
# ------------------------------------------------
# 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
#

#
# require: dialog, BLT
#
# NOTES:
# All the functions that are ``private'' to this file are prefixed
# with page:_, and SHOULD NOT be called by outside routines (ok if 
# passed to bind scripts).
#
# PUBLIC INTERFACE:
#
# page:display
#
# PRIVATE INTERFACE:
#
# page:_create
# page:_reload
# page:_item_extract_info
# page:_check_sanity
# page:_goto_prev
# page:_goto_next
# page:_find_next_editable_field
# page:_edit_item
# page:_update_value
# page:_change_value
# page:_update_and_pop_submenu
# page:_update_and_chase_submenu
# page:_chase_submenu
#


#
# file browser routines.
#

#
# Pop up a directory selection dialog (filedlg:)
#
proc filedlg:browse {value_w} {
    # FIXME: For newer tk releases >= 4.2
    global tk_version
    if {$tk_version < 4.2} {
	set dialog [tix filedialog]
	set fsbox [$dialog subwidget fsbox]
	$fsbox configure -pattern "*" -value ""
	$dialog config -command "filedlg:select_file $value_w"
	$dialog popup
    } else {
	# globbing does not work on windows as on Unix
	global tcl_platform
	set platform $tcl_platform(platform)
	if {$platform == "unix"} {
	    set types {
		{{Misc Data Files}		{.dat}}
		{{SHADOW Workspace Files}	{.ws}}
		{{SHADOW Namelist Files}	{start.\[0-9\]\[0-9\]}}
		{{SHADOW Source Image}		{begin.dat}}
		{{SHADOW Image Files}		{star.\[0-9\]\[0-9\]}}
		{{SHADOW Image Files}		{mirr.\[0-9\]\[0-9\]}}
		{{All Files}			*}
	    }
	} else {
	    set types {
		{{Misc Data Files}		{.dat}}
		{{SHADOW Workspace Files}	{.ws}}
		{{SHADOW Namelist Files}	{start.*}}
		{{SHADOW Source Image}		{begin.dat}}
		{{SHADOW Image Files}		{star.*}}
		{{SHADOW Image Files}		{mirr.*}}
		{{All Files}			*}
	    }
	}
	set file [tk_getOpenFile \
	    -title "Select file" \
	    -filetypes $types
	]
	if {$file != ""} {
	    filedlg:select_file $value_w $file
	}
    }
}

proc filedlg:select_file {value_w file} {
    $value_w delete 0 end
    $value_w insert 0 [fix_platform_filename $file]
    focus $value_w
}

#
# proc for managing popup menus for enumerated types.
#
proc page:_make_enum_popup {btn value_w page item} {
    set this_item [xitem list $page $item]
    set cur_value [xitem vget $page $item]
    set vallowed [xitem vallowed $page $item]
    set menu ${btn}.menu
    if ![winfo exists $menu] {
	vputs "creating new menu for $this_item"
        menu $menu -tearoff 0
	foreach val $vallowed {
	    $menu add command -label $val \
	        -command "set gvars(enumerated_popup) \"$val\""
	}
    }
    # set default index.
    set cur_index [lsearch $vallowed $cur_value]
    if {$cur_index == -1} {
        puts stderr "Bad enumerated type. Internal error."
	set cur_index 0
    }
    set x [expr {round([winfo rootx $btn] + [winfo width $btn]/2.0)}]
    set y [expr {round([winfo rooty $btn] + [winfo height $btn]/2.0)}]
    tk_popup $menu $x $y $cur_index 

    global gvars
    tkwait variable gvars(enumerated_popup)
    $value_w delete 0 end
    $value_w insert 0 $gvars(enumerated_popup)
    page:_goto_next $value_w $page $item
}

#
# Helper to extract item info from [xitem list <page_name> <item_name>]
#
set xitem_list_index(item_type)	0	;#       TEXT/DATA/SKIP
set xitem_list_index(text)	1	;# TEXT: text 
set xitem_list_index(text_submenu)	2	;# TEXT: text 
set xitem_list_index(record)	1	;# DATA: OE/SCR/SRC
set xitem_list_index(name)	2	;# DATA: MenuItem name
set xitem_list_index(type)	3	;# DATA: varable type (not used)
set xitem_list_index(prompt)	4	;# DATA: prompt to display 
set xitem_list_index(n/a)	5	;# DATA: applicable or not
set xitem_list_index(force_update) 6	;# DATA: reload current page
set xitem_list_index(readonly)	7	;# DATA: read-only item?
set xitem_list_index(value)	8	;# DATA: current value
set xitem_list_index(submenu)	9	;# DATA: submenu name

#
# ITEM_EXTRACT_ITEM:
# example: set item_type [extract [xitem list <page> <item>] item_type]
#
proc page:_item_extract_info {info what} {
    global xitem_list_index
    if [catch "set xitem_list_index([string tolower $what])" result] {
	dialog .error "Internal Error" \
	    "Bad Item type \"$what\" (Report bug)." error 0 Dismiss
	return "ITEM BUG"
    }
    return [lindex $info $result]
}

#
# PAGE_SANITY_CHECK: checks for the usual screw-ups.
#
# CHECK/FIXME: Check page case sensitivity.
#
proc page:_check_sanity {page_name} {
    set page_names [string toupper [xmenu pages]]
    if {[lsearch -exact $page_names [string toupper $page_name]] == -1} {
	error "No such page \"$page_name\" exist. Check menu file."
    }
}

#
# PAGE_GOTO_PREV: edit previous field.
#
# the window w is the name of the current ENTRY widget, and I have to
# figure out the index of the current field to be able to update it
# in the corresponding current value field.
#
# the format of ENTRY widget name (cf: page:_create) ==>
#        ${page_frame}.${item_index}
#
# the format of VALUE widget name (cf: page:_create) ==>
#        ${page_frame}.${item_index}_value
#
proc page:_goto_prev {w page_name item_name} {
    if {[page:_update_value $w $page_name $item_name]} {return 1}
    #
    # break up a window path into lists of sub-paths
    # eg., .top.page.source.4 ==> {top page source 4}
    #
    set pieces [split $w "."]

    #
    # get the last element of the list, which is the item index, and also
    # keep the stub (everything until the last element) so we can also
    # get the VALUE widget name.
    #
    set cnt [lindex $pieces [expr [llength $pieces] - 1]]
    set stubw [join [lrange $pieces 0 [expr [llength $pieces] - 2]] "."]

    set new_index [page:_find_next_editable_field $page_name [expr $cnt - 1] up]
    focus ${stubw}.${new_index}
    set gvars(curpageitem) $new_index
    return 0
}

#
# PAGE_GOTO_NEXT: edit next field.
#
# the window w is the name of the current ENTRY widget, and I have to
# figure out the index of the current field to be able to update it
# in the corresponding current value field.
#
# the format of ENTRY widget name (cf: page:_create) ==>
#        ${page_frame}.${item_index}
#
# the format of VALUE widget name (cf: page:_create) ==>
#        ${page_frame}.${item_index}_value
#
proc page:_goto_next {w page_name item_name} {
    global gvars
    if {[page:_update_value $w $page_name $item_name]} {return 1}
    #
    # break up a window path into lists of sub-paths
    # eg., .top.page.source.4 ==> {top page source 4}
    #
    set pieces [split $w "."]

    #
    # get the last element of the list, which is the item index, and also
    # keep the stub (everything until the last element) so we can also
    # get the VALUE widget name.
    #
    set cnt [lindex $pieces [expr [llength $pieces] - 1]]
    set stubw [join [lrange $pieces 0 [expr [llength $pieces] - 2]] "."]

    set new_index \
	[page:_find_next_editable_field $page_name [expr $cnt + 1] down]
    focus ${stubw}.${new_index}
    set gvars(curpageitem) $new_index
    return 0
}

#
# PAGE_FIND_NEXT_EDITABLE_ITEM:
#
# Given a starting item number in a page, find the next item index that
# is an editable field (ie., DATA item). Returns the new index if found,
# or the initial starting point if wraps around.
#
proc page:_find_next_editable_field {page_name {start 1} {dir down}} {
    set item_names [xpage items $page_name]
    set num_items [llength $item_names]
    set start_item $start
    if {$start_item < 1} {
	set start_item $num_items
    } elseif {$start_item > $num_items} {
	set start_item 1
    }

    set searched 1
    set found 0
    if {$dir == "down"} {set increment 1} {set increment -1}
    while {$found == 0 && $searched < $num_items} {
	set item_name [lindex $item_names [expr $start_item - 1]]
	set this_item [xitem list $page_name $item_name]
	set item_type [page:_item_extract_info $this_item item_type]
	set item_na [page:_item_extract_info $this_item n/a]
	set item_readonly [page:_item_extract_info $this_item readonly]
	if {$item_type == "DATA" && \
	    !$item_readonly && $item_na != "n/a" \
	} {
	    set found 1
	} else {
	    incr searched
	    incr start_item $increment
	    if {$start_item < 1} {
		set start_item $num_items
	    } elseif {$start_item > $num_items} {
		set start_item 1
	    }
	}
    }
    return $start_item
}

#
# PAGE_EDIT_ITEM: edit a particular numbered item.
#
proc page:_edit_item {page_w page_name {item_no 1}} {
    set new_index [page:_find_next_editable_field $page_name $item_no down]
    set value_w ${page_w}.${new_index}
    set gvars(curpageitem) $new_index
    focus $value_w
}

#
# PAGE_UPDATE_VALUE: update the value from user entry.
#
# Update the value from a particular ENTRY widget, and if successful (ie.,
# correct value supplied), update the VALUE widget as well.
#
# return 0 if successful, 1 if not.
#
#
proc page:_update_value {w page_name item_name} {
    set retcode 0
    set new_value [$w get]
    if {[string length [string trim $new_value]] > 0} {
	set ok [page:_change_value $page_name $item_name $new_value]
	$w delete 0 end
	if {$ok == 0}  {
	    set value_w ${w}_value
	    set new_value [xitem vget $page_name $item_name]
	    $value_w configure -text [format "%-20s" $new_value]
	    #
	    # for now always reload the current page.
	    #
	    global gvars gprefs
	    set this_item [xitem list $page_name $item_name]
	    set force_update [page:_item_extract_info $this_item force_update]
	    if {$force_update} {
		page:_reload $gvars(cur_page_w) $page_name
	    }
	} else {
	    set retcode 1
	}
    }
    return $retcode
}

#
# PAGE_CHANGE_VALUE: validate and change the value of a page item
#
# returns:
#	0: new value updated
#	1: no change
#      -1: error
#
proc page:_change_value {page_name item_name args} {
    #
    # take care for values with embedded spaces in it. Have to first
    # join and then pass inside of quotes to "xitem vset". Also trim
    # only the right side, not left.
    #
    set new_value [string trimright [join $args]]
    set item_value [xitem vget $page_name $item_name]
    if {[string compare $item_value $new_value] == 0} {
	return 1
    }
    if {[catch "xitem vset $page_name $item_name \"$new_value\"" msg] != 0} {
	 set allowed_values [xitem vallowed $page_name $item_name]
	 dialog .allowed {Allowed values} \
 	    "Bad value \"$new_value\". Allowed values ==>\n$allowed_values" \
 	    {} 0 Dismiss
	return -1
    }
    global gvars
    set gvars(modified) 1
    return 0
}

#
# PAGE_UPDATE_AND_POP_SUBMENU: update current and go up the page stack
#
proc page:_update_and_pop_submenu {w page_name item_name} {
    global gvars
    if {[llength $gvars(pagestack)] != 0} {
	if {[page:_update_value $w $page_name $item_name]} {
	    return 1
	} else {
	    pop_page_stack
	    return 0
	}
    } else {
	return [page:_goto_prev $w $page_name $item_name]
    }
}

#
# PAGE_UPDATE_AND_CHASE_SUBMENU: update current and go down a submenu
#
proc page:_update_and_chase_submenu {w page_name item_name submenu} {
    if {[page:_goto_next $w $page_name $item_name]} {return 1}
    #
    # CHECK/FIXME: Should the above be replaced by the following?
    #if {[page:_update_value $w $page_name $item_name]} {return 1}
    #
    page:_chase_submenu $submenu
    return 0
}

#
# PAGE_CHASE_SUBMENU: go down a submenu.
#
proc page:_chase_submenu {page_name} {
    global gvars
    # push the page on stack so we can come back to it.
    push_curpage $gvars(cur_page_w) $gvars(curpage) $gvars(curpageitem)

    # and display the submenu in its place.
    page:display $gvars(cur_page_w) $page_name
}

#
# DISPLAY_PAGE: display the menu page ``page_name''. (Assume it exists)
#
# Arguments:
# page_frame -	 The frame (pre-existing) containing the menu page widgets.
# page_name -	 Name of MENU page (eg., SOURCE, OE, SCR)
# item_to_edit - Defaults to the first (editable) item on the page.
#
proc page:display {page_frame page_name {item_to_edit 1}} {
    global gvars
    set_msg "Displaying menu page \"$page_name\"..."

    catch "destroy $page_frame"
    set page_frame [page:_create $page_frame $page_name]

    # make this the current page
    set gvars(curpage) $page_name

    # and pack and all that.
    # pack $page_frame -anchor w -expand 0 -fill both -ipadx 10 -ipady 10
    pack $page_frame
    if {$item_to_edit != -1} {
	#
	# this might return error if the current page is fake and we
	# are called from redraw_current_page or some such callback.
	#
	catch "page:_edit_item $page_frame $page_name $item_to_edit"
    }
    set_msg "Displaying menu page \"$page_name\"...done."
    return $page_frame
}

proc page:_create_about {page_frame} {
    global gvars env
    #
    # create the body of the display.
    #
    frame $page_frame -class XMenuAboutPage -bd 10 ;#-relief raised -bd 2
    set title [label ${page_frame}.title -justify center -relief raised \
	-font -*-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-* \
	-padx 10 -pady 10 \
	-text "SHADOW GUI Version 1.1.0"]

    set body [label ${page_frame}.body -justify left -padx 10 -pady 10]
    $body config -text {

    Use HELP | DEMO to load and run one the supplied demos.

    To construct a beamline, use the ToolBox on your left and add SOURCE
    and Optical Elements to the Beamline at bottom left. To add a SOURCE,
    simply click on the "Source" icon in Toolbox; to add an OE, now click
    on the OE icon in Toolbox. Note how the "Beamline" area changes as
    you add new elements. Each element you add will go *after* the
    currently selected item in the "Beamline"; if OE 1 is selected (drawn
    in a different color), adding another OE will make the new one OE 2.

    Once you have the beamline constructed, you can edit the parameters
    of the items in "Beamline" by double-clicking on the icons (or use
    the right mouse button to use the popup menu) and a parameter page
    will appear in the "Parameter Editor" window.

    To generate the SOURCE rays, click on the "Ray_Trace/Source" in
    Toolbox. To trace the rays through the OEs, use "Ray_Trace/System".
    If you want to trace an invidual OE, simply use the popup-menu (press 
    right mouse button, button 3, in the "Beamline" area and select "Run" 
    option).

    Now you can add various inspectors and view the results; eg., to view 
    the mirror parameters for OE 1, highlight "OE 1" icon and click on
    "MirInfo" in Toolbox. Now you can simply Run it.

    }
    pack $page_frame.title -anchor c -fill x -expand yes -side top

    # load the SHADOW banner if needed.
    if {$gvars(shadow_image) == ""} {
	set image [image create photo]

	global tcl_version
	if {$tcl_version >= 7.5} {
	    set imagefile [file join $gvars(shadow_tcl_library) \
	        bitmaps shadow.gif]
	} else {
	    # NON-PORTABLE-HACK. WILL NOT WORK ON MACs.
	    set imagefile $gvars(shadow_tcl_library)/bitmaps/shadow.gif
	}
	if [catch "$image read $imagefile" msg] {
	    puts stderr "Warning: Cannot find SHADOW image ($msg)"
	} else {
	    set gvars(shadow_image) $image
	}
    }
    set shadowimage [label ${page_frame}.img -image $gvars(shadow_image)]
    pack $page_frame.img -anchor c -fill x -expand yes -side top \
        -padx 10 -pady 10
    pack $page_frame.body -anchor c -fill both -expand yes -side top \
        -padx 10 -pady 10
    return $page_frame
}

proc page:_create_annotation {page_frame} {
    global gvars env workspace
    #
    # create the body of the display.
    #
    frame $page_frame -class XMenuAnnotation -bd 10 ;#-relief raised -bd 2
    if {[string trim $workspace(title)] == ""} {
        set title "file: $workspace(filename)"
    } else {
        set title $workspace(title)
    }
    set title [label ${page_frame}.title -justify center -relief raised \
	-font -*-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-* \
	-padx 10 -pady 10 -bd 1 \
	-text "$title"]

    set subtitle [label ${page_frame}.subtitle -justify center \
	-font -*-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-* \
	-text "$workspace(subtitle)"]

    if {[string trim $workspace(annotation)] == ""} {
        set annotation {
	No Annotation Provided. Use the WORKSPACE | ANNOTATE to add
	annotation about this particular workspace.
	}
    } else {
        set annotation $workspace(annotation)
    }
    set width [expr $gvars(page_w_wt) - 60]
    set body [label ${page_frame}.body -wraplength $width \
        -justify left -padx 10 -pady 10]
    $body config -text $annotation

    pack $page_frame.title -anchor c -fill x -expand yes -side top \
        -padx 10 -pady 10
    pack $page_frame.subtitle -anchor c -fill x -expand yes -side top \
        -padx 10 -pady 10
    pack $page_frame.body -anchor c -fill both -expand yes -side bottom \
        -padx 10 -pady 10
    return $page_frame
}


#
# CREATE_PAGE: create the menu page ``page_name''.
#
# This procedure creates the frame containing all the widgets for
# MENU page <page_name>. Returns the frame <page_frame> that it creates.
#
# Arguments:
# page_frame -	The frame (created here) to put all the stuff in.
# page_name -	Name of MENU page (eg., SOURCE, OE, SCR)
#
proc page:_create {page_frame page_name} {
    global gvars 

    set_msg "Creating menu page \"$page_name\"..."
    #
    # check for special pages, such as about:, etc.
    #
    switch -exact $page_name {
        "about:" {
	    return [page:_create_about $page_frame]
	}
        "annotation:" {
	    return [page:_create_annotation $page_frame]
	}
	default { }
    }
    
    page:_check_sanity $page_name
    xpage eval $page_name
    set page_title [xpage title $page_name]
    set page_type [xpage type $page_name]
    set item_names [xpage items $page_name]
    set num_items [llength $item_names]

    #
    # width of text lines, and other widgets on each menu item. Prompt
    # should be at least 40 maybe? (CHECK)
    #
    set text_width 34
    set curval_width 18
    set entry_width 15

    #
    # maximum number of columns in the page. It's increased by one if the
    # n/a column is shown.
    #
    set max_cols 5
    if $gvars(show_na) {incr max_cols}

    #
    # create the body of the display.
    #
    frame $page_frame -class XMenuPage

    label $page_frame.title -text [format "%-40s" $page_title] \
        -font $gvars(page_bold_font)  -relief raised
    if {$page_type == "OE"} {
	set subtitle "OE $gvars(cur_oe)"
    } elseif {$page_type == "SCR"} {
	set subtitle "SCREEN $gvars(cur_scr) of OE $gvars(cur_oe)"
    } else {
	set subtitle ""
    }

    #
    # TODO: Use the max_cols instead of hard-coding the column span
    #
    if {$subtitle != ""} {
	set title_cspan 2
	if $gvars(show_na) {incr title_cspan}
	set title_col 0

	set subtitle_cspan 2
	set subtitle_col 2
	if $gvars(show_na) {incr subtitle_col}

	label $page_frame.detail -text $subtitle \
	    -font $gvars(page_bold_font)
	grid $page_frame.title \
	    -row 0 -column $title_col \
	    -columnspan $title_cspan
	grid $page_frame.detail \
	    -row 0 -column $subtitle_col \
	    -columnspan $subtitle_cspan
    } else {
	grid $page_frame.title \
	    -row 0 -column 0 \
	    -columnspan $max_cols
    }

    #
    # now create all the items.
    #
    # items are placed in a grid using the BLT table manager (ncols = 5).
    #    SKIP: empty label
    #    TEXT: corresponding text
    #    DATA: PROMPT(%40s) CUR_VALUE(%20s) N/A(%3s) EDIT_FIELD(%20s) SUBMENU
    #
    set cnt 1
    foreach item_name $item_names {
	set this_item [xitem list $page_name $item_name]
	set item_type [page:_item_extract_info $this_item item_type]
	switch -exact -- $item_type {
	SKIP {
	    set padded [format "%-${text_width}s" ""]
	    set item_line \
	    	[label $page_frame.$cnt -text "$padded" -width $text_width]
	    grid $item_line \
	        -row $cnt -column 0 \
		-columnspan $max_cols
	}
	TEXT {
	    set item_text [page:_item_extract_info $this_item text]
	    set item_submenu [page:_item_extract_info $this_item text_submenu]
	    set padded [format "%-${text_width}s" $item_text]
	    set item_line [label $page_frame.$cnt -text $padded \
		-width $text_width -anchor w] 
	    grid $item_line \
	        -row $cnt -column 0 \
		-columnspan 1 \
		-sticky w
	    if {[string trim $item_submenu] != ""} {
		#
		# change the -command to update page before jumping
		# to the submenu.
		# -command "page:_chase_submenu $item_submenu"
		#
		set submenu \
		    [button $page_frame.${cnt}_submenu \
			-text ">>" -font $gvars(page_bold_font) \
			-width 3 \
			-command \
			"page:_chase_submenu $item_submenu"]
		set col [expr $max_cols - 1]
		grid $submenu \
		    -row $cnt -column $col \
		    -columnspan 1 \
		    -sticky w
	    }
	}
	DATA {
	    set item_prompt [page:_item_extract_info $this_item prompt]
	    set item_na [page:_item_extract_info $this_item n/a]
	    set item_readonly [page:_item_extract_info $this_item readonly]
	    set item_value [page:_item_extract_info $this_item value]
	    set item_submenu [page:_item_extract_info $this_item submenu]
	    set mark_inactive 0
	    if {$item_readonly || $item_na == "n/a"} {
	        set mark_inactive 1
	    }

	    set col 0
	    #
	    # accept 40 characters for the font, but use only 34 or so 
	    # for now. TODO/CHECK: Eventually make it auto-scrollable?
	    #
	    set padded [format "%-${text_width}s" "$item_prompt"]
	    set prompt [label $page_frame.${cnt}_prompt \
		-text $padded -width $text_width -anchor w \
		-font $gvars(page_bold_font)]
	    set gvars(normal_text_fg) [$prompt cget -foreground]
	    if {$item_na == "n/a"} {
	        $prompt configure -fg [tix option get disabled_fg]
	    }
	    grid $prompt \
	        -row $cnt -column $col \
		-columnspan 1 \
		-sticky w
	    incr col 1

	    if $gvars(show_na) {
		set padded [format "%-3s" "$item_na"]
		set na \
		    [label $page_frame.${cnt}_na -text $padded -width 3]
		grid $na \
		    -row $cnt -column $col \
		    -columnspan 1 \
		    -sticky w
		incr col
	    }

	    #
	    # Make the CURRENT value 18 characters instead of the 20 or 
	    # so. Note that the value widget is only 15 characters now.
	    #
	    set padded [format "%-${curval_width}s" "$item_value"]
	    set cur_value [label $page_frame.${cnt}_value \
		-text $padded -width $curval_width -anchor w \
		-font $gvars(page_italic_font)]
	    if {$item_na == "n/a"} {
	        $cur_value configure -fg [tix option get disabled_fg]
	    }
	    grid $cur_value \
		-row $cnt -column $col \
		-columnspan 1 \
		-sticky w
	    incr col

	    set value [entry $page_frame.${cnt} -relief sunken \
		-width $entry_width]
	    grid $value \
		-row $cnt -column $col \
		-columnspan 1 \
		-sticky w

	    set gvars(normal_entry_bg) [$value cget -background]
	    if $mark_inactive {
		$value configure -state disabled
		$value configure -bg [tix option get disabled_fg]
	    }
	    incr col

	    bind $value <Return> \
		"page:_goto_next %W $page_name $item_name"
	    bind $value <Tab> \
		"page:_goto_next %W $page_name $item_name"
	    bind $value <Key-Down> \
		"page:_goto_next %W $page_name $item_name"
	    bind $value <Key-Up> \
		"page:_goto_prev %W $page_name $item_name"
	    bind $value <Meta-Key-Left> \
	    	"page:_update_and_pop_submenu %W $page_name $item_name; \
	    	break"
	    bind $value <Key-less> \
		"page:_update_and_pop_submenu %W $page_name $item_name; \
		break"
	    bind $value <Control-n>	\
		"page:_goto_next %W $page_name $item_name"
	    bind $value <Control-p> \
		"page:_goto_prev %W $page_name $item_name"
	    bind $value <Any-question> \
		"item_help %W $page_name $item_name; break"


	    # special thingies for specific types.
	    set var_type [page:_item_extract_info $this_item type]
	    switch -exact -- $var_type {
		"filename" {
		    set button [button $page_frame.${cnt}_btn \
			-command "filedlg:browse $value" \
			-takefocus 1 \
			-bitmap [tix getbitmap openfile]]
		    grid $button \
			-row $cnt -column $col \
			-columnspan 1 \
			-sticky w
		    if $mark_inactive {
			$button configure -state disabled
		    }
		}
		"enumerated" {
		    set vallowed [xitem vallowed $page_name $item_name]
		    set button $page_frame.${cnt}_btn
		    button $button \
			-command "page:_make_enum_popup $button \
			    $value $page_name $item_name" \
			-takefocus 1 \
			-bitmap [tix getbitmap cbxarrow]
		    # the following lets you single click
		    bind $button <1> "tkButtonInvoke $button; break"
		    bind $button <ButtonRelease-1> "break"
		    grid $button \
			-row $cnt -column $col \
			-columnspan 1 \
			-sticky w
		    if $mark_inactive {
			$button configure -state disabled
		    }
		}
	    }
	    incr col

	    if {$item_submenu == ""} {
		set submenu \
		    [label $page_frame.${cnt}_submenu -width 3]
		bind $value <Meta-Key-Right> \
		    "page:_goto_next %W $page_name $item_name; \
		    break"
		bind $value <Key-greater> \
		    "page:_goto_next %W $page_name $item_name; \
		    break"
	    } else {
		#
		# change the -command to update page before jumping
		# to the submenu.
		# -command "page:_chase_submenu $item_submenu"
		#
		set submenu \
		    [button $page_frame.${cnt}_submenu \
			-text ">>" -font $gvars(page_bold_font) \
			-width 3 \
			-command \
			"page:_update_and_chase_submenu $value \
			$page_name $item_name $item_submenu"]

		bind $value <Meta-Key-Right> \
		    "page:_update_and_chase_submenu %W \
		    $page_name $item_name $item_submenu; \
		    break"
		bind $value <Key-greater> \
		    "page:_update_and_chase_submenu %W \
		    $page_name $item_name $item_submenu; \
		    break"
	    }
	    grid $submenu \
		-row $cnt -column $col \
		-columnspan 1 \
		-sticky w
	    incr col
	}
	default {
	    error "page:_create: Unknown menu item type \"$item_type\"."
	}
	}
	incr cnt
    }
    #
    # Add the Back button after some empty space.
    #
    set item_line \
	[label $page_frame.$cnt -text "" -width $text_width]
    grid $item_line \
	-row $cnt -column 0 \
	-columnspan $max_cols
    incr cnt

    set gvars(backbtn) \
	[button $page_frame.${cnt}_backbtn \
	    -text "<<" -font $gvars(page_bold_font) -width 3 \
	    -command "pop_page_stack" \
	]
    $gvars(balloon) bind $gvars(backbtn) \
        -balloonmsg "Return to last page" \
        -statusmsg "Click to return to last page"
    grid $gvars(backbtn) \
	-row $cnt -column 4 \
	-columnspan 1 \
	-sticky w
    if {[llength $gvars(pagestack)] == 0} {
        $gvars(backbtn) configure -state disabled
    }

    set_msg "Creating menu page \"$page_name\"...done."
    grid propagate $page_frame 0
    return $page_frame
}
#
# RELOAD_PAGE: reloads the values into the current page.
#
# This procedure reloads all the values for <page_name> from MENU 
# back-end into the VALUE fields. The back-end responds with the
# values for the currently selected OE or SCR, if appropriate.
#
# Arguments:
# page_frame -	The frame (pre-existing) containing the menu page widgets.
# page_name -	Name of MENU page (eg., SOURCE, OE, SCR)
#
#
proc page:_reload {page_frame page_name} {
    global gvars
    page:_check_sanity $page_name
    xpage eval $page_name
    set page_title [xpage title $page_name]
    set item_names [xpage items $page_name]
    set num_items [llength $item_names]

    #
    # width of text lines, and other widgets on each menu item. Prompt
    # should be at least 40 maybe? (CHECK)
    #
    set text_width 34
    set curval_width 18
    set entry_width 15

    #
    # now reload values into the curvalue fields.
    #
    set cnt 1
    foreach item_name $item_names {
	set this_item [xitem list $page_name $item_name]
	set item_type [page:_item_extract_info $this_item item_type]
	if {$item_type == "DATA"} {
	    set item_na [page:_item_extract_info $this_item n/a]
	    set item_readonly [page:_item_extract_info $this_item readonly]
	    set item_value [page:_item_extract_info $this_item value]

	    set mark_inactive 0
	    if {$item_readonly || $item_na == "n/a"} {set mark_inactive 1}

	    if $gvars(show_na) {
		set padded [format "%-3s" "$item_na"]
		set na $page_frame.${cnt}_na
		$na configure -text $padded
	    }

	    set padded [format "%-${curval_width}s" "$item_value"]
	    set cur_value $page_frame.${cnt}_value
	    $cur_value configure -text $padded
	    set entry_widget $page_frame.${cnt}
	    set prompt_widget $page_frame.${cnt}_prompt
	    set curval_widget $page_frame.${cnt}_value
	    #
	    # special buttons are the extra controls for "enumerated",
	    # "filename" etc variables.
	    #
	    set special_button ""
	    set var_type [page:_item_extract_info $this_item type]
	    switch -exact -- $var_type {
		"filename" {
		    set special_button $page_frame.${cnt}_btn 
		}
		"enumerated" {
		    set special_button $page_frame.${cnt}_btn
		}
	    }
	    if $mark_inactive {
		$entry_widget configure -state disabled
		$entry_widget configure -bg [tix option get disabled_fg]
		if {$item_na == "n/a"} {
		    $prompt_widget configure -fg [tix option get disabled_fg]
		    $curval_widget configure -fg [tix option get disabled_fg]
		}
		if {$special_button != ""} {
		    $special_button configure -state disabled
		}
	    } else {
		$entry_widget configure -state normal
		$entry_widget configure -bg $gvars(normal_entry_bg)
		if {$item_na == "n/a" || 1} {
		    $prompt_widget configure -fg $gvars(normal_text_fg)
		    $curval_widget configure -fg $gvars(normal_text_fg)
		}
		if {$special_button != ""} {
		    $special_button configure -state normal
		}
	    }
	}
	incr cnt
    }
    return $page_frame
}
