#!/bin/sh
# The next line restarts using expectk \
exec expectk "$0" "$@"

#***********
#  The comments with the code here are meant to address the details of the
#  code itself, not the functionality of XsimView.  Refer to the XsimView's 
#  man page for a discussion of functionality.
#
#  The overall algorithm of XsimView is described at the bottom of the code
#  below where the "main" of XsimView is given.  The anciliary procedures 
#  are desribed in turn below.
#-----------


#**********
# source     fileSelect.src
#
# why:       fileSelect provides the ability to save files.  Here, it
#            is used to allow the user to select a filename so that in.simv,
#            the temporary file used for output, can be saved to whatever
#            name they desire.

# All from fileSelect.src
# Begins the file loading process.  Can take one argument, fileName
# which would specify the file being loaded, in which case,
# fileSelect process is skipped and the file is directly loaded.
proc loadFile {{file {}} {type {}}} {
	global globals
	set globals(optList) {}
	# if file is empty, ask user for new file, if still empty, exit
	# since they must have hit cancel or something else went wrong
	if {$file == {}} {set file [fileSelect Load]}
	if {$file == {}} {return}

	# if type is nil, then 
	# find the final letters after last . to get file type
	if {$type == {}} {
		set type [string range [file extension $file] 1 end]
	}
	if {$globals(options)} {
		switch -- $type {
			mv		{set globals(optList) \
						[optionRead $globals(read_blif_mvOptions)]}
			blif	{set globals(optList) \
						[optionRead $globals(read_blifOptions)]}
			vl		{set globals(optList) \
						[optionRead $globals(read_verilogOptions)]}
			fair	{set globals(optList) \
						[optionRead $globals(read_fairnessOptions)]}
			ctl	{set globals(optList) \
						[optionRead $globals(model_checkOptions)]}
		}
	}
	switch -- $type {
		mv		{Send "rlmv $globals(optList) $file"}
		blif	{Send "read_blif $file"}
		vl		{Send "read_verilog $file"}
		fair	{Send "read_fairness $file"}
		ctl	{Send "model_check $file"}
		default	{loadFile "$file [giveFileType $file]"}
	}
}

proc writeFile {{file {}} {type {}}} {
	global globals
	set globals(optList) {}
	# if file is empty, ask user for filename to write to.
	# If still empty, then they must have hit cancel or something
	if {$file == {}} {set file [fileSelect Write]}
	if {$file == {}} {return}

	# find the final letters after last . to get file type
	set type [string range [file extension $file] 1 end]
	if {$globals(options)} {
		switch -- $type {
			mv		{set globals(optList) \
						[optionRead $globals(write_blif_mvOptions)]}
			blif	{set globals(optList) \
						[optionRead $globals(austin_write_blifOptions)]}
		}
	}
	switch -- $type {
			mv		{Send "write_blif_mv $globals(optList) $file"}
			blif	{Send "austin_write_blif $globals(optList) $file"}
			default	{writeFile "$file.[giveFileType $file]"}
	}
}

proc giveFileType {file} {
	global temp globals
	toplevel .q
	set gFTDone 0

	wm title .q "What type of file?"
	set short [string range $file [expr [string last / $file] + 1] end]
	message .q.msg -text "What type is $short ?"
	pack .q.msg -side top
	bind .q <Return> {set gFTDone 1}

	# Set up radio buttons for type of file to load
	frame .q.radio
	foreach type {{blif blif} {blif_mv mv}  {verilog vl} {fairness fair}} {
		radiobutton .q.radio.[lindex $type 1] -variable temp \
			-text [lindex $type 0] -value [lindex $type 1]
		pack .q.radio.[lindex $type 1] -side top
	}
	pack .q.radio -side top

	# Create the OK button
	# The OK button has a rim to indicate it is the default
	frame .q.button -bd 10
	frame .q.button.ok -bd 2 -relief sunken
	button .q.button.ok.b -text OK -command {set gFTDone 1}
	pack .q.button.ok.b
	pack .q.button.ok -side top
	pack .q.button -side top

	tkwait variable gFTDone
	destroy .q
	return $temp
}

proc fileSelect {why {default {}}} {
	global fileSelect globals
	set globals(options) 0
	set fileSelect(why) $why
	catch {destroy .fileSelect}
	set t [toplevel .fileSelect -bd 4]

	wm title $t $why
	message $t.msg -aspect 1000 -text $why
	pack $t.msg -side top -fill x

	# Create a read_only entry for the current pwd
	set fileSelect(dirEnt) [entry $t.dir -width 15 \
		-relief flat -state disabled]
	pack $t.dir -side top -fill x

	# Create an entry for the pathname
	# The value is kept in the fileSelect(path)
	frame $t.top
	label $t.top.l -text "File:" -padx 0
	set e [entry $t.top.path -relief sunken -textvariable fileSelect(path)]
	pack $t.top -side top -fill x
	pack $t.top.l -side left
	pack $t.top.path -side right -fill x -expand true
	set fileSelect(pathEnt) $e

	# Use $e to set up bindings for entry widget
	bind $e <Return> fileSelectOK
	bind $e <Control-c> fileSelectCancel
	bind $e <space> fileSelectComplete
	focus $e

	# Create a listbox to hold the directory contents
	listbox $t.list -yscrollcommand [list $t.scroll set]
	scrollbar $t.scroll -command [list $t.list yview]
	# A single click copies the name into the entry
	# A double-click selects the name
	bind $t.list <Button-1> {fileSelectClick %y}
	bind $t.list <Double-Button-1> {
		fileSelectClick %y;
		fileSelectOK
	}

	# Create the OK and cancel buttons and options button
	# The OK button has a rim to indicate it is the default
	frame $t.buttons -bd 10
	frame $t.buttons.ok -bd 2 -relief sunken
	button $t.buttons.ok.b -text OK -command fileSelectOK
	button $t.buttons.cancel -text Cancel -command fileSelectCancel
	checkbutton $t.buttons.options -text "Choose Options" \
		-variable globals(options)

	# Pack the list, scrollbar and button box
	# in a horizontal stack below the upper widgets
	pack $t.list -side left -fill both -expand true
	pack $t.scroll -side left -fill both
	pack $t.buttons -side left -fill both
	pack $t.buttons.ok $t.buttons.cancel $t.buttons.options -side top \
		-padx 10 -pady 5
	pack $t.buttons.ok.b -padx 4 -pady 4

	# Initialize variables in the directory
	set fileSelect(path) {}
	set dir [pwd]

	set fileSelect(dir) {}
	set fileSelect(done) 0

	# Wait for the listbox to be visible so we
	# can provide feedback during the listing
	tkwait visibility .fileSelect.list
	fileSelectList $dir

	tkwait variable fileSelect(done)
	destroy .fileSelect
	return $fileSelect(path)
}

# Creates the list of dirs and files to put into the listbox, if not given
proc fileSelectList { dir {files {}} } {
	global fileSelect

	# Update the directory
	set e $fileSelect(dirEnt)
	$e config -state normal
	$e delete 0 end
	$e insert 0 $dir
	$e config -state disabled

	# Give the user some feedback
	set fileSelect(dir) $dir
	.fileSelect.list delete 0 end
	.fileSelect.list insert 0 Listing...

	.fileSelect.list delete 0
	# If files is undefined, then match all files in current dir
	if {[string length $files] == 0} {
		# List the directory and add an entry for the parent dir.
		set files [glob -nocomplain $fileSelect(dir)/*]
		.fileSelect.list insert end ../
	}

	# Take files, and sort all dirs to front
	set dirs {}
	set others {}
	foreach f [lsort $files] {
		if [file isdirectory $f] {
			lappend dirs [file tail $f]/
		} else {
			lappend others [file tail $f]
		}
	}

	# Insert all dirs and files into the listbox
	foreach f [concat $dirs $others] {
		.fileSelect.list insert end $f
	}
}


# Checks files specified, and whether or not to return a value, or
# make a new listbox.  Basically, first check if command is to go to 
# parent directory.  If not, check if fileSelect(path) is a directory.
# If so, create new list.  If not, check if is a file.  If so, return
# the file, else, try some magic to go somewhere else. (also known
# as file name completion.
proc fileSelectOK {} {
	global fileSelect

	# Handle the special case of the parent directory
	if {[regexp {\.\./} $fileSelect(path)]} {
		set fileSelect(path) {}
		fileSelectList [file dirname $fileSelect(dir)]
		return
	}

	# create a full path from fileSelect(dir) and fileSelect(path)
	set path $fileSelect(dir)/$fileSelect(path)
	if [file isdirectory $path] {
		set fileSelect(path) {}
		fileSelectList $path
		return
	}
	if [file exists $path] {
		set fileSelect(path) $path
		set fileSelect(done) 1
		return
	}

	# Neither a file nor a directory
	# See if a glob will find something
	# catch returns true if something goes wrong
	if [catch {glob $path} files] {
		# Okay, maybe they typed in a new absolute pathname and we
		# should try fileSelect(path) as the ENTIRE path name
		if [catch {glob $fileSelect(path)} path] {
			# Okay, nothing there, if we're writing, then accept as 
			# as new filename, else try filename completion.
			if {[string match $fileSelect(why) Write]} {
				set fileSelect(done) 1
				return
			} else {
				# Try file name completion
				fileSelectComplete
				return
			}
		} else {
		# OK - there are files there, so let's reassign the dir and path
		set fileSelect(dir) [file dirname $fileSelect(path)]
		set fileSelect(path) [file tail $fileSelect(path)]
		fileSelectOK
		return
		}
	} else {
		# Current directory has stuff in it, catch returned no error
		# Ok - current directory is ok, select file or list 'em.
		if {[llength [split $files]] == 1} {
			set fileSelect(path) $files
			fileSelectOK
		} else {
			set fileSelect(dir) [file dirname [lindex $files 0]]
			fileSelectList $fileSelect(dir) $files
		}
	}
}

# Quits out of fileSelection and moves on.
proc fileSelectCancel {} {
	global fileSelect
	set fileSelect(done) 1
	set fileSelect(path) {}
}

# Find the file the user clicked on.
proc fileSelectClick { y } {
	# Take the item the user clicked on
	global fileSelect
	set l .fileSelect.list
	set fileSelect(path) [$l get [$l nearest $y]]
	focus $fileSelect(pathEnt)
}

# Big nasty routine to do file name completion
proc fileSelectComplete {} {
	global fileSelect

	# Do name completion, Nuke something.
	set fileSelect(path) [string trim $fileSelect(path) \t\ ]

	# Figure out what directory we are looking at,
	# dir is directory, tail is partial name
	if {[string match /* $fileSelect(path)]} {
		set dir [file dirname $fileSelect(path)]
		set tail [file tail $fileSelect(path)]
	} elseif [string match ~* $fileSelect(path)] {
		if [catch {file dirname $fileSelect(path)} dir] {
			return;		# Bad user
		}
		set tail [file tail $fileSelect(path)]
	} else {
		set path $fileSelect(dir)/$fileSelect(path)
		set dir [file dirname $path]
		set tail [file tail $path]
	}

	# See what files are there
	set files [glob -nocomplain $dir/$tail*]
	if {[llength [split $files]] == 1} {
		# Matched a single file
		set fileSelect(dir) $dir
		set fileSelect(path) [file tail $files]
	} else {
		if {[llength [split $files]] > 1} {
			# Find the longest common prefix
			set l [expr [string length $tail]-1]
			set miss 0
			# remember that files has absolute paths
			set file1 [file tail [lindex $files 0]]
			while {$miss == 0} {
				incr l
				if {$l == [string length $file1]} {
					# file1 is a prefix of all others
					break
				}
				set new [string range $file1 0 $l]
				foreach f $files {
					if ![string match $new* [file tail $f]] {
						set miss 1
						incr l -1
						break
					}
				}
			}
			set fileSelect(path) [string range $file1 0 $l]
		}
		fileSelectList $dir $files
	}
}


#**********
# procedure match
#
# input:     string       is a string of characters
#            file         is a string of characters that holds a file name.
#
# function:  open the file given by input "file" and iteratively look for
#            a text line in "file" that a) does not start with a "#", and 
#            b) whose first word (text string) matches input "string".  Upon
#            finding such a line that meets this criteria, procedure match
#            returns a 1.  If EOF is reached before a line meeting the said
#            criteria is found, then procedure match returns a 0.
#            
proc match {string file} {

    set f [open $file r]

    while {1} {

	set cnt [gets $f inline]
	if {$cnt ==  -1} {
	    close $f
	    return 0
	}
	set y [split $inline {}]
	set y1 [lindex $y 0]
	if {$y1 != "#"} {
	    if {$string ==  $inline} {
		close $f
		return 1
	    }
	}
    }
}
 

#********************
#  procedure parse
#
#  input     sf       string that holds name of sim output file.
#  
#  output    none
#
#  function  described with the code below.
proc parse {sf} {
 

# the following are globals.
# observe                 is to hold all of the variables eligible for display.
# ninps, nlats, nouts     are to hold the number of inputs, latches, outputs.
# cy                      is to hold the variable values for each cycle.
# ncy                     is to hold the number of cycles 
# what                    is to hold the identity of each variable (input, latch, output).
 
	global observe ninps nlats nouts
	global cy ncy
	global what

# the following globals are the display preference variables.
# see the man page for a discussion of these variables.
	global bgtop bg1 bg2
	global fgtop fg1 fg2
	global fonttop font1 font2
	global hlinescolor vlinescolor
	global cybarbg cybarfg
 
# open the sim output file for parsing.
	set f [open $sf r]
 
# find the .inputs, .latches, .outputs, and .initial lines in the sim output file
# and make lists out of them.
	set finps 0
	set flats 0
	set fouts 0
	set finit 0
	while {$finps == 0 || $flats == 0 || $fouts == 0 || $finit == 0}  {
	    set cnt [gets $f inline]
	    if {$cnt == -1} {break}
	    split $inline
	    set first [lindex $inline 0]
	    switch $first {
		".inputs" {
		    set inps [lreplace $inline 0 0]
		    set ninps [llength $inps]
		    set finps 1
		    for {set j 0} {$j < $ninps} {incr j} {
			lappend what (I)
		    }
		}
		".latches" {
		    set lats [lreplace $inline 0 0]
		    set nlats [llength $lats]
		    set flats 1
		    for {set j 0} {$j < $nlats} {incr j} {
			lappend what (L)
		    }
		}
		".outputs" {
		    set outs [lreplace $inline 0 0]
		    set nouts [llength $outs]
		    set fouts 1
		    for {set j 0} {$j < $nouts} {incr j} {
			lappend what (O)
		    }
		}
		".initial" {
			set finit 1
		}
	    }
	}
 
# if any of the .inputs, .latches, .outputs, or .initial lines are not
# found, exit with an Error.
	if {$finps ==  0} {
		puts "ERROR: No .inputs line was found!"
		return -1
	}
	if {$flats ==  0} {
		puts "ERROR: No .latches line was found!"
		return -1
	}
	if {$fouts ==  0} { 
		puts "ERROR: No .outputs line was found!"
		return -1
	}
	if {$finit ==  0} {
		puts "ERROR: No .initial line was found!"
		return -1
	}
 
# list observe holds all the variables eligible to be viewed.
# here the list of variables is dumped to the strike file (strk.simv)
# so user can later access and select variables to strike.
	set observe [concat $inps $lats $outs]
	set ff [open strk.simv w]
	foreach i $observe {
	    puts $ff $i
	}
	close $ff

# Set defaults for the display preferences.
#
	set bgtop Grey 
	set bg1 White
	set bg2 Black
	set fgtop Black
	set fg1 Black
	set fg2 White 
	set fonttop -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 
	set font1 -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 
	set font2 -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 
	set hlinescolor White 
	set vlinescolor Yellow 
	set cybarbg Blue 
	set cybarfg White 

# And put them in the preferences file.
#
	set ff [open prefs.simv w]

	puts $ff "bgtop $bgtop"
	puts $ff "bg1 $bg1"
	puts $ff "bg2 $bg2"
	puts $ff "fgtop $fgtop"
	puts $ff "fg1 $fg1"
	puts $ff "fg2 $fg2"
	puts $ff "fonttop $fonttop"
	puts $ff "font1 $font1"
	puts $ff "font2 $font2"
	puts $ff "hlinescolor $hlinescolor"
	puts $ff "vlinescolor $vlinescolor"
	puts $ff "cybarbg $cybarbg"
	puts $ff "cybarfg $cybarfg"

	close $ff
 
# the following lines of code look for the beginning of the portion of
# the sim output file that contains the cycle information and upon 
# finding it stores it in "array" for further processing.
	set i 0
	while {1} {
		set cnt [gets $f inline]
		if {$cnt ==  -1} {break}
		set y [split $inline {}]
		set y1 [lindex $y 0]
		if {$y1 ==  {} || $y1 ==  "#" || $y1 ==  "."} {continue}
		set array($i) $inline
		incr i 1
	}
	set ncy $i
	close $f

# if no cycle lines were found, exit with an Error.
	if {$cnt ==  -1 && $i ==  0} {
		puts "ERROR: No simulation results found!"
		return -1
	}

# the following lines of code parse the contents of "array" into
# "cy".  each list in "array" is itself a list of strings, this step turns
# the cycle information so that each element in "cy" is a simple list of 
# strings. 
	for {set j 0} {$j < $i} {incr j} {
	    set z [split $array($j) \;]
	    set p [lindex $z 0]
	    set q [lindex $z 1]
	    set r [lindex $z 2]
	    set cy($j) [concat $p $q $r]
	}
 
}
 
#*************
# procedure strike
#
# inputs      sf      string that holds file name
# 
# outputs     none
#
# function    Reads the contents of the file pointed to by input "sf" and
#             based on its contents generates an array of flags that indicate
#             whether a given variable should be displayed.  The array of 
#             flags is in one-to-one correspondence with the array of variables
#             that are eligible to be displayed.
proc strike {} {
 
    global observe
    global show nshow
 
    set nshow 0
 
    set nobvs [llength $observe]
    set j 0
    for {set i 0} {$i < $nobvs} {incr i} {
	set m [match [lindex $observe $i] strk.simv]
	if {$m ==  0} {
	    lappend show 0
	} else {
	    lappend show 1
	    incr nshow
	}
    }
}
 
#*************
# procedure readprefs
#
# inputs      pf       string that holds file name
#
# outputs     none
#
# function    Reads the contents of the file pointed to by input "pf" and
#             sets the values of the preferences accordingly.  See the "man"
#             page for description of the preferences currently supported.
#
proc readprefs {} {
 
    global bgtop bg1 bg2
    global fgtop fg1 fg2
    global fonttop font1 font2
    global hlinescolor vlinescolor
    global cybarbg cybarfg
    global run

    set bgtop 0
    set bg1 0
    set bg2 0
    set fgtop 0
    set fg1 0
    set fg2 0
    set fonttop 0
    set font1 0
    set font2 0
    set hlinescolor 0
    set vlinescolor 0
    set cybarbg 0
    set cybarfg 0
 
    set f [open prefs.simv r]
 
    while {1} {

	set cnt [gets $f inline]
	if {$cnt ==  -1} {break}
	set first [lindex $inline 0]
 
	switch $first {
	    bgtop { set bgtop [lindex $inline 1] }
	    bg1 { set bg1 [lindex $inline 1] }
	    bg2 { set bg2 [lindex $inline 1] }
	    fgtop { set fgtop [lindex $inline 1] }
	    fg1 { set fg1 [lindex $inline 1] }
	    fg2 { set fg2 [lindex $inline 1] }
	    fonttop { set fonttop [lindex $inline 1] }
	    font1 { set font1 [lindex $inline 1] }
	    font2 { set font2 [lindex $inline 1] }
	    hlinescolor { set hlinescolor [lindex $inline 1] }
	    vlinescolor { set vlinescolor [lindex $inline 1] }
	    cybarbg { set cybarbg [lindex $inline 1] }
	    cybarfg { set cybarfg [lindex $inline 1] }
	}
    }

    close $f

}

#****************
# procedure findlongestvar
#
# input        none
#
# output       none
#
# function     This procedure operates on the parsed lists of variables names and
#              values for each cycle to find a) the longest variable name, and b) 
#              the longest value of any given variable.  These values are then used
#              to set the corresponding field widths in the display.
#
proc findlongestvar {} {
    
    global observe
    global show
    global cywidth varwidth
    global cy ncy

    set nobvs [llength $observe]

    set maxlen 0
    for {set i 0} {$i < $nobvs} {incr i} {
	set active [lindex $show $i]
	if {$active == 1} {
	    set str [lindex $observe $i]
	    set len [string length $str]
	    if {$len > $maxlen} {set maxlen $len}
	}
    }
    set varwidth [expr ($maxlen+2)*0.25]

    set maxlen 0
    for {set i 0} {$i < $ncy} {incr i} {
	for {set j 0} {$j < $nobvs} {incr j} {
	    set active [lindex $show $j]
	    if {$active == 1} {
		set str [lindex $cy($i) $j]
		set len [string length $str]
		if {$len > $maxlen} {set maxlen $len}
	    }
	}
    }
# Takes care of finding the maximum width for cycle width
    set cywidth_cycleInfo [expr (log($ncy)+2)*0.25]
    set cywidth_dataInfo [expr ($maxlen+2)*0.25]

    if {$cywidth_cycleInfo > $cywidth_dataInfo} {
        set cywidth $cywidth_cycleInfo
    } else {
        set cywidth $cywidth_dataInfo
    }

}

#***************
# procedure drawcanvas
#
# input       none
# 
# output      none
#
# function    This procedure carries out the actual displaying.  It is described alongside
#             with the code below.
#
proc drawcanvas {} {

#---------
# these globals are the display preferences, they are read
# from file dprefs by procedure readprefs:
#
# varwidth	holds the width of the left-most column that
#		lists the variables under observation
# cywidth	holds the width of the cycle columns that
#		lists the value of the variables for each
#		cycle.
#

    global varwidth
    global cywidth
 
#---------
# the following globals hold the items to be
# displayed:
#
# cy 		is the array of lists, each element is a list 
#    		of the value of the items for that cycle,
# ncy 	        is the number of elements in cy,
# show 	        is an array that marks those elements in cy and
#    		observe that are to be displayed, (i.e. after
#		some of the elements in observe are striken based
#		on the contents of the strike file.
# nshow 	is the number of "1" elements in show,
# observe 	is a list of _all_ the items that _can_ be displayed.
# what          is an array that identifies a variable as either an input, latch, or output.
#
    global cy ncy
    global show nshow
    global observe
    global what
 

#-------------
# the following globals are the prefrences variables.
#
    global bgtop bg1 bg2
    global fgtop fg1 fg2
    global fonttop font1 font2
    global hlinescolor vlinescolor
    global cybarbg cybarfg

#------------
# here the top level widget that holds the results display is
# created and the two canvases that will be used to construct
# the display are defined.

# this width and height is a good start for a workstation monitor,
# of course, the window can be resized in the usual way.
#
    set wwidth 800
    set wheight 500

    global infile
    global t
    set t .top
    toplevel $t 
    wm geometry $t ${wwidth}x${wheight}
    wm title $t "XsimView 0.1"
    set c1 $t.canvas1
    set c2 $t.canvas2

# the top of the display holds generic information.
#
    text $t.text -font $fonttop -bg $bgtop -fg $fgtop
    text $t.text1 -font $fonttop -bg $bgtop -fg $fgtop
    $t.text insert 1.0 "XSimView Rev. 0.1 (April 1996)\n"
    $t.text insert 2.0 "by J.S. Duran\n"
    $t.text insert 3.0 "ECE Department\n"
    $t.text insert 4.0 "UT Austin\n"
    $t.text1 insert 1.0 "Displaying file: $infile"
    place $t.text -x 0 -y 0 -relwidth 1.0
    place $t.text1 -relx 0.5 -y 0
 
#---------------
# build the higher level canvas (holds the variable names at the left-most
# of the display and also holds the lower level canvas).
#
    set c1w [expr $wwidth]
    set c1h [expr $nshow+1]
    set c1dims [list 0 0 ${c1w} ${c1h}c]
    canvas $c1 -relief sunken -borderwidth 2 -scrollregion $c1dims -yscrollcommand "$t.vscroll set" -bg ${bg1}
    scrollbar $t.vscroll -command "$c1 yview"
    place $t.vscroll -relx 0.98 -rely 0.15 -relheight 0.85 -width 0.5c
    place $c1 -x 0 -rely 0.15 -relheight 0.85 -relwidth 1.0
 
    set f $c1.frame
    frame $f -relief sunken -borderwidth 2 
    $c1 create window ${varwidth}c 0 -anchor nw -height ${c1h}c -width ${wwidth} -window $f

#------------
# build the lower level canvas (holds the per cycle variable value information).
#
    set c2xlen [expr $ncy*$cywidth*1.25]
    set c2scrl [list 0 0 ${c2xlen}c ${c1h}c]
    set hsw [expr $wwidth-$varwidth*40]
    canvas $f.c2 -relief sunken -borderwidth 2 -scrollregion $c2scrl -xscrollcommand "$t.hscroll set" -bg ${bg2}
    place $f.c2 -x 0 -y 0 -relheight 1.0 -relwidth 1.0
    scrollbar $t.hscroll -orient horiz -command "$f.c2 xview"
    place $t.hscroll -x 0 -rely 0.125 -height 0.5c -relwidth 1.0

#------------
# place the variable names on the higher level canvas.  also draw the horizontal grid
# lines in the lower level canvas.
#
    set q [llength $show]
    set v 1
    for {set i 0} {$i < $q} {incr i} {
	set sh [lindex $show $i]
	if {$sh ==  1} {
	    set item1 [lindex $observe $i]
	    set item2 [lindex $what $i]
	    set item $item1$item2    
	    $c1 create text ${varwidth}c ${v}c -text $item -anchor ne -fill $fg1 -font $font1
	    $f.c2 create line 0 [expr $v+0.4]c ${c2xlen}c [expr $v+0.4]c -fill $hlinescolor
	    incr v
	}
    }

#-----------
# place the per cycle data on the lower level canvas.  also draw the vertical grid lines on the
# lower level canvas and generate the cycle number bar at the top of the lower level canvas.
#
    set over1 [expr $cywidth*0.5]
    set over2 $cywidth
    set xtx $over1
    set xln $over2
    set xrc1 0
    set xrc2 $over2
    for {set i 0} {$i < $ncy} {incr i} {
	$f.c2 create rectangle ${xrc1}c 0 ${xrc2}c 0.5c -fill $cybarbg -outline $vlinescolor
	set cylabel "CY$i"
	$f.c2 create text ${xtx}c 0.25c -width ${cywidth}c -text $cylabel -fill $cybarfg -justify center -font $font2
	$f.c2 create line ${xln}c 0 ${xln}c ${c1h}c -fill $vlinescolor
	set v 1
	for {set k 0} {$k < $q} {incr k} {
	    set sh [lindex $show $k]
	    if {$sh ==  1} {
		set item [lindex $cy($i) $k]
		$f.c2 create text ${xtx}c ${v}c -width ${cywidth}c -text $item -fill $fg2 -justify center -font $font2
		incr v
	    }
	}
	set xtx [expr $xtx+$cywidth]
	set xln [expr $xln+$cywidth]
	set xrc1 [expr $xrc1+$cywidth]
	set xrc2 [expr $xrc2+$cywidth]
    }

    unset show

}


#############################################
# main body of XsimView
#

#-------
# parse the command line (either one argument -- sim output file name --
# or none.
#

global infile
global t

if {$argc == 0} {
    set infile "in.simv"
    puts "setting input file to in.simv\n"
} elseif {$argc == 1} {
    set infile [lindex $argv 0]
} else {
    puts "syntax is:  xsimview <filename>\n"
    exit
}

######################################################
##### FILES NECESSARY FOR SAVING
######################################################

proc doSave {} {
	set file [fileSelect Write]
	catch {exec cp in.simv $file}
}

####################################################
##### END FILES NECESSARY FOR SAVING
####################################################

#------------------
# initialization sequence.
# 
# parse the sim output file (only needs to be done once).
# open the main window for XsimView
# 
parse $infile
wm title . "XsimView 0.1"
button .go -text GO -command {
    strike
    findlongestvar
    readprefs
    drawcanvas
}
button .redraw -text ReDraw -command {
    destroy $t
    strike
    findlongestvar
    readprefs
    drawcanvas
}
button .save -text Save -command {doSave}
button .quit -text Quit -command {destroy .}

pack .go -side left -padx 2m -pady 2m
pack .redraw -side left -padx 2m -pady 2m
pack .save -side left -padx 2m -pady 2m
pack .quit -side left -padx 2m -pady 2m

















