#         Portable Batch System (PBS) Software License
# 
# Copyright (c) 1999, MRJ Technology Solutions.
# All rights reserved.
# 
# Acknowledgment: The Portable Batch System Software was originally developed
# as a joint project between the Numerical Aerospace Simulation (NAS) Systems
# Division of NASA Ames Research Center and the National Energy Research
# Supercomputer Center (NERSC) of Lawrence Livermore National Laboratory.
# 
# Redistribution of the Portable Batch System Software and use in source
# and binary forms, with or without modification, are permitted provided
# that the following conditions are met:
# 
# - Redistributions of source code must retain the above copyright and
#   acknowledgment notices, this list of conditions and the following
#   disclaimer.
# 
# - Redistributions in binary form must reproduce the above copyright and 
#   acknowledgment notices, this list of conditions and the following
#   disclaimer in the documentation and/or other materials provided with the
#   distribution.
# 
# - All advertising materials mentioning features or use of this software must
#   display the following acknowledgment:
# 
#   This product includes software developed by NASA Ames Research Center,
#   Lawrence Livermore National Laboratory, and MRJ Technology Solutions.
# 
#         DISCLAIMER OF WARRANTY
# 
# THIS SOFTWARE IS PROVIDED BY MRJ TECHNOLOGY SOLUTIONS ("MRJ") "AS IS" WITHOUT 
# WARRANTY OF ANY KIND, AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED.
# 
# IN NO EVENT, UNLESS REQUIRED BY APPLICABLE LAW, SHALL MRJ, NASA, NOR
# THE U.S. GOVERNMENT BE LIABLE FOR ANY DIRECT DAMAGES WHATSOEVER,
# NOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# This license will be governed by the laws of the Commonwealth of Virginia,
# without reference to its choice of law rules.

# NOTE: we restrict to one element keys only for efficiency!
proc box {frame_name {args}} {

  set boxName [string trim $frame_name "."]

  global $boxName canvas sysinfo
  set configure 0

  set argc [llength $args]
  for {set i 0} {$i < $argc} {incr i} {
    switch -exact -- [lindex $args $i] {
      configure  {
                set configure 1
      }
      -title {
                set [set boxName](title) [lindex $args [incr i]]
      }
      title {
		if { ![info exists [set boxName](title)]} {
			return ""
		}
                return [set [set boxName](title)]
      }
      -class {
                set [set boxName](class) [lindex $args [incr i]]
      }
      class {
		if { ![info exists [set boxName](class)]} {
			return Plain
		}
                return [set [set boxName](class)]
      }
      titlelabel {
		if { ![info exists [set boxName](titlelabel)]} {
			return ""
		}
                return [set [set boxName](titlelabel)]
      }		
      -key {
                set [set boxName](key) [lindex $args [incr i]]
      }
      key {
		if { ![info exists [set boxName](key)]} {
			return 0
		}
                return [set [set boxName](key)]
      }
      -entrylabels {
                set [set boxName](entrylabels) [lindex $args [incr i]]
      }
      entrylabels {
		if { ![info exists [set boxName](entrylabels)]} {
			return ""
		}
                return [set [set boxName](entrylabels)]
      }
      -lboxlabels {
                set [set boxName](lboxlabels) [lindex $args [incr i]]
      }
      lboxlabels {
		if { ![info exists [set boxName](lboxlabels)]} {
			return ""
		}
                return [set [set boxName](lboxlabels)]
      }
      -lboxwidths {
                set [set boxName](lboxwidths) [lindex $args [incr i]]
      }
      lboxwidths {
		if { ![info exists [set boxName](lboxwidths)]} {
			return 0
		}
                return [set [set boxName](lboxwidths)]
      }
      -lboxheights {
                set [set boxName](lboxheights) [lindex $args [incr i]]
      }
      lboxheights {
		if { ![info exists [set boxName](lboxheights)]} {
			return 0
		}
                return [set [set boxName](lboxheights)]
      }
      -orient {
                set [set boxName](orient) [lindex $args [incr i]]
      }
      orient {
		if { ![info exists [set boxName](orient)]} {
			return "x"
		}
                return [set [set boxName](orient)]
      }
      -grabBox {
                set [set boxName](grabBox) [lindex $args [incr i]]
      }
      grabBox {
		if { ![info exists [set boxName](grabBox)]} {
			return ""
		}
                return [set [set boxName](grabBox)]
      }
      -selindex {
                set [set boxName](selindex) [lindex $args [incr i]]
      }
      selindex {
		if { ![info exists [set boxName](selindex)]} {
			return -1
		}
                return [set [set boxName](selindex)]
      }
      vscroll {
		if { ![info exists [set boxName](vscroll)]} {
			return ""
		}
                return [set [set boxName](vscroll)]
      }
      ncols {
		if { ![info exists [set boxName](ncols)]} {
			return 1
		}
                return [set [set boxName](ncols)]
      }
      -nrows {
		set lbox [box $frame_name lbox 0]
		if { [string compare $lbox ""] != 0 } {
			set [set boxName](nrows) [$lbox size]
		}

		incr [set boxName](nrows) [lindex $args [incr i]]
                return [set [set boxName](nrows)]
      }
      nrows {
		set lbox [box $frame_name lbox 0]
		if { [string compare $lbox ""] == 0 } {
			return 0
		}
		set [set boxName](nrows) [$lbox size]

                return [set [set boxName](nrows)]
      }
      lbox {
		set index [lindex $args [incr i]]
		if {![info exists [set boxName](lbox,$index)]} {
			return ""
		}
                return [set [set boxName](lbox,$index)]
      }
      entry {
		set index [lindex $args [incr i]]
		if {![info exists [set boxName](entry,$index)]} {
			return ""
		}
                return [set [set boxName](entry,$index)]
      }
      -entryval {
		set index [lindex $args [incr i]]
		set eval [lindex $args [incr i]]
                set [set boxName](entryval,$index) $eval
                return [set [set boxName](entryval,$index)]
      }
      entryval {
		set index [lindex $args [incr i]]
		if {![info exists [set boxName](entryval,$index)]} {
			return ""
		}
                return [set [set boxName](entryval,$index)]
      }
      -entryvalDeleted {
                set [set boxName](entryvalDeleted) [lindex $args [incr i]]
		return
      }
      entryvalDeleted {
		if { ![info exists [set boxName](entryvalDeleted)]} {
			return ""
		}
                return [set [set boxName](entryvalDeleted)]
      }
      -noUpdateButton {
                set [set boxName](noUpdateButton) [lindex $args [incr i]]
      }
      noUpdateButton {
		if { ![info exists [set boxName](noUpdateButton)]} {
			return 0
		}
                return [set [set boxName](noUpdateButton)]
      }
      -addCmd {
                set [set boxName](addCmd) [lindex $args [incr i]]
      }
      addCmd {
		if { ![info exists [set boxName](addCmd)]} {
			return ""
		}
                return [set [set boxName](addCmd)]
      }
      -remCmd {
                set [set boxName](remCmd) [lindex $args [incr i]]
      }
      remCmd {
		if { ![info exists [set boxName](remCmd)]} {
			return ""
		}
                return [set [set boxName](remCmd)]
      }
      updateButton {
		if { ![info exists [set boxName](updateButton)]} {
			return ""
		}
                return [set [set boxName](updateButton)]
      }
      create     {
                catch {unset $boxName}
      }
      unset {
                catch {unset $boxName}
		return
      }
      getBoxArray {
                return "[set boxName]"
      }
    }
  }

  # create one frame to hold everything
  set f [frame $frame_name -class [box $frame_name class]]
  if { ![info exists sysinfo(boxes)] || \
	[lsearch -exact $sysinfo(boxes) $f] == -1 } {
  		lappend sysinfo(boxes) $f
  }

  $f configure -relief ridge -borderwidth 2
  frame $f.t -class CmdFrame -relief raised -borderwidth 2
  frame $f.b -relief flat
  frame $f.b.e -relief flat
  frame $f.b.l -relief ridge -bd 4
  frame $f.b.b -relief flat
  
  label  $f.t.label -text [box $f title] -justify center
  pack $f.t.label -anchor nw -side left -expand 1 -fill both
  set [set boxName](titlelabel) $f.t.label

## build the entry widget 

## build the add button
  button $f.b.e.add -text add -height 2 \
	-command "boxAdd $f \"[box $f addCmd]\""

  if {[string compare [box $f orient] x] == 0} {
  	pack $f.b.e.add -side right -anchor c -pady 1m
  } else {
  	pack $f.b.e.add -side bottom -anchor c -pady 1m
  }
  set k 0
  set packlist ""
  set entrylabels [box $f entrylabels]
  set lboxwidths [box $f lboxwidths]
  set lboxheights [box $f lboxheights]
  foreach labelinfo $entrylabels {
	set elabel [lindex $labelinfo 0]
	set type [lindex $labelinfo 1]
 	set args [lrange $labelinfo 2 end]
	frame $f.b.e.$k

	switch -exact -- $type {
      	  MENU_ENTRY     {
		menuEntry $f.b.e.$k create -menuvalues $args -title $elabel \
					-textvariable [set boxName](entryval,$k)
  		set [set boxName](entry,$k) ""
      	  }
	  default {
		set lboxwidth [lindex $lboxwidths $k]
  		set einfo [buildFullEntrybox $f.b.e.$k [string length $elabel] \
				$elabel $lboxwidth "" bottom 0 top right 0 c]

  		set eframew [lindex $einfo 0]
  		set elabelw [lindex $einfo 1]
  		set eentryw  [lindex $einfo 2]

		global sel$eentryw
		set sel$eentryw 0
  		$eentryw configure -textvariable [set boxName](entryval,$k)
  		$elabelw configure -anchor nw
  		set [set boxName](entry,$k) $eentryw
  		bind_entry_overselect $eentryw
		register_default_action $eentryw $f.b.e.add
	  }
	}
	set packlist "$packlist $f.b.e.$k"
	incr k
  }
  set [set boxName](ncols) $k
  set [set boxName](nrows) 0
  if {[string compare packlist ""] != 0} {
  	eval pack [string trim $packlist] -side left -anchor nw
  }
  boxSetTabbing $f


# build the listbox
  set packlist ""
  set k 0

  set lboxlabels [box $f lboxlabels] 
  set llen [llength $lboxlabels]
  foreach llabel $lboxlabels {
	frame $f.b.l.$k
	set lboxwidth [lindex $lboxwidths $k]
	set lboxheight [lindex $lboxheights $k]
	if {[expr $k + 1] != $llen} {
  		set listBoxInfo [buildFullListbox $f.b.l.$k \
		       ${lboxwidth}x${lboxheight} $llabel xscroll 0 0 0]
	} else {
  		set listBoxInfo [buildFullListbox $f.b.l.$k \
		       ${lboxwidth}x${lboxheight} $llabel xyscroll 0 0 0]
  		set [set boxName](vscroll) [lindex $listBoxInfo 4]
	}
  	set hostsListbox          [lindex $listBoxInfo 3]
  	set hostsListboxSelButton [lindex $listBoxInfo 2]
  	set hostsListboxLabel     [lindex $listBoxInfo 1]

  	bind_listbox_single_select $hostsListbox
  	bind_listbox_select $hostsListbox $f
  	set [set boxName](lbox,$k) $hostsListbox
  	set packlist "$packlist $f.b.l.$k"
	incr k
  }
  if {[string compare packlist ""] != 0} {
  	eval pack [string trim $packlist] -side left -anchor nw
  } 

  boxSetScroll $f

## build the buttons

  set noUpdateButton [box $f noUpdateButton]
  if {$noUpdateButton} {
  	set buttonList [list  {delete "delete"}]
  } else {
  	set buttonList [list  {delete "delete"} {info "update"}]
  }

  set cmdButtons [buildCmdButtons $f.b.b [list $buttonList] y 0 11 1 1 0 0]

  set cmdButtonFrame  [lindex $cmdButtons 0]
  set cmdButtonDelete [lindex $cmdButtons 1]
  
  if {!$noUpdateButton} {
  	set cmdButtonUpd   [lindex $cmdButtons 2]
  	set [set boxName](updateButton) $cmdButtonUpd
  }

  $cmdButtonDelete configure -command "boxDelete $f \"[box $f remCmd]\""
  
  set fwidth [expr [lindex [$hostsListbox configure -height] end]*$canvas(bigLabelFontHeight)] 

  if {[string compare [box $f orient] x] == 0} {
  	pack $f.b.e -pady [expr $fwidth/2] -padx 0 -side left -anchor nw
  	pack  $f.b.l $f.b.b -side left -anchor nw \
			-padx $canvas(bigLabelFontWidth)
  } else {
  	pack $f.b.e -side top -anchor nw -pady 0 -padx 0
        pack $f.b.l $f.b.b -side left -after $f.b.e -pady 3m -anchor nw -padx 0
  }
  pack $f.t -anchor nw -side top -fill both -expand 1
  pack $f.b -anchor nw -side top -fill none -expand 0

}

proc boxesUnset {} {
	global sysinfo

	if [info exists sysinfo(boxes)] {
		foreach b $sysinfo(boxes) {
			box $b unset
		}
	}
	set sysinfo(boxes) ""
}

proc boxAdd {frame_name {addfunc ""}} {
	global tk_version dialog


	set entrylabels [box $frame_name entrylabels]
	set keylist [box $frame_name key]
	set ncols [box $frame_name ncols]
	set keyval ""
	set keys ""
	for {set i 0} {$i < $ncols} {incr i} {
		 box $frame_name -entryval $i \
			[cleanstr [box $frame_name entryval $i]]
		set kindex [lsearch -regexp $keylist "(^| )$i"]
		if {$kindex != -1} {
			set entryval [box $frame_name entryval $i]

			if {[string compare $entryval ""] == 0} {
				set entrylab [lindex $entrylabels $i]
				set entryw [box $frame_name entry $i]
				popupInfoBox [box $frame_name grabBox] \
				  "Can't have an empty \"$entrylab\" entry!!!" \
				  			500 $entryw
				return
			
			}
			set klist [split [lindex $keylist $kindex] ":"]
			set k [lindex $klist 0]
			lappend keys $k
			set kmatchval [lindex $klist 1]

			lappend keyval $entryval
			if {[string compare $kmatchval ""] != 0 && \
			    		[regexp "^($kmatchval)$" $entryval]} {
				set kunique($k) 1
			} else {
				set kunique($k) 0
			}
		}
	}
	set nrows [box $frame_name nrows]
	for {set i 0} {$i < $nrows} {incr i} {
		set boxval ""
		for {set j 0} {$j < $ncols} {incr j} {

			if {[lsearch -exact $keys $j] != -1} {
			
				set entryv [box $frame_name entryval $j]
				set entryw [box $frame_name entryval $j]
				set lbox [box $frame_name lbox $j]
				if {$kunique($j)} {
				    if {![lboxvalue_isUnique $lbox $entryv]} {
					popupInfoBox [box $frame_name grabBox] \
					"\"$entryv\" already specified in one of the fields!" 500 $entryw
					return
				    }
				}
				lappend boxval [$lbox get $i]
			}
		}
		if {[lcomp $keyval $boxval] == 0} {
			popupInfoBox [box $frame_name grabBox] \
			"Unable to add entry: key \"[concat $keyval]\" was duplicated!" 500 [box $frame_name entry 0]
			return
		}
	}

	for {set i 0} {$i < [box $frame_name ncols]} {incr i} {
		set lbox [box $frame_name lbox $i]
		set entry [box $frame_name entry $i]

		global sel$entry

		$lbox insert end [box $frame_name entryval $i]
		set sel$entry 1
	}
	box $frame_name -nrows +1
	boxSelect $frame_name end

	if {[string compare $addfunc ""] != 0} {
		eval $addfunc
	}
}

proc boxDelete {frame_name {remfunc ""}} {
	for {set i 0} {$i < [box $frame_name ncols]} {incr i} {
		set lbox [box $frame_name lbox $i]
		set selindex [$lbox curselection]
		if {[string compare $selindex ""] != 0} {
			box $frame_name -entryvalDeleted [$lbox get $selindex]
			$lbox delete $selindex
			boxSelect $frame_name $selindex
		}
	}
	box $frame_name -nrows -1
	if {[string compare $remfunc ""] != 0} {
		eval $remfunc
	}
}
proc boxGetCurselect {frame_name index} {
	set selindex [box $frame_name selindex]
	if {$selindex == -1} {
		return
	}

	set lbox [box $frame_name lbox $index]
	catch {$lbox get $selindex} value
	return $value
}

proc boxSelect {frame_name index} {
	global tk_version

	set nrows [box $frame_name nrows]
	if { $index == -1 } {

		set lbox [box $frame_name lbox 0] 
		global cmd$lbox
		if [info exists cmd$lbox] {
                	eval [set cmd$lbox]
        	}
		return
	}

	set boxName [box $frame_name getBoxArray]
	global $boxName

        set [set boxName](selindex) $index
	set selindex $index

	for {set i 0} {$i < [box $frame_name ncols]} {incr i} {
		set lbox [box $frame_name lbox $i]
		set entry [box $frame_name entry $i]
	        if { $tk_version < 4.0 } {
          		$lbox select from $selindex
        	} else {
			catch {$lbox selection clear [$lbox curselection]}
			$lbox select set $selindex $selindex
		}

		set boxval [$lbox get $selindex]
		if {[string compare $boxval ""] != 0} {
			box $frame_name -entryval $i $boxval
		}

		if {$i == 0 && [string compare $entry ""] != 0} {
			focus $entry
			$entry select from 0
			$entry select to end

			if {$tk_version >= 4.0} {
      				$entry xview moveto 1
			}
		}
		global cmd$lbox
        
		if [info exists cmd$lbox] {
                	eval [set cmd$lbox]
        	}
	}


}
proc boxSetScroll {frame_name} {
	global tk_version

	set vscroll [box $frame_name vscroll]
	if {[string compare $vscroll ""] == 0} {
		return
	}
	set lboxes ""
	for {set i 0} {$i < [box $frame_name ncols]} {incr i} {
		set lbox [box $frame_name lbox $i]
		$lbox configure -yscrollcommand "$vscroll set"

		lappend lboxes $lbox
	}
	$vscroll configure -command "boxAdjScrollView [list $lboxes]"
}

proc boxSetTabbing {f} {

	for {set i 0} {$i < [box $f ncols]} {incr i} {
		set entry [box $f entry $i]
		if {[string compare $entry ""] == 0} {
			continue
		}
		set nentry [box $f entry [expr $i+1]]
		if {[string compare $nentry ""] == 0} {
			set nentry $entry
		} 
		set pentry [box $f entry [expr $i-1]]
		if {[string compare $pentry ""] == 0} {
			set pentry $entry
		}
		bind_entry_tab $entry $nentry $pentry
	}
}


proc boxAdjScrollView {args} {
        set lboxes [lindex $args 0]
        set params [lrange $args 1 end]
        foreach b $lboxes {
                eval $b yview $params
        }
}
