# Platform specific setup for tcl scripts
# Copyright (c) 1999 Andrew Chang
# %W% %@%

proc bk_init {} \
{
	global	tcl_platform dev_null tmp_dir wish sdiffw file_rev
	global	file_start_stop file_stop line_rev keytmp file_old_new
	global 	bk_fs env

	if [catch {wm withdraw .} err] {
		puts "DISPLAY variable not set correctly or not running X"
		exit 1
	}

	set sdiffw [list "sdiff" "-w1" ]
	set dev_null "/dev/null"
	set wish "wish"
	set tmp_dir  "/tmp"
	set keytmp "/var/bitkeeper"

	# Stuff related to the bk field seperator: ^A
	set bk_fs |
	set file_old_new {(.*)\|(.*)\|(.*)}
	set line_rev {([^\|]*)\|(.*)}

	set file_start_stop {(.*)@(.*)\.\.(.*)}
	set file_stop {(.*)@([0-9.]+$)}
	set file_rev {(.*)@([0-9].*)}
	set env(BK_GUI) "YES"
}
proc getConfig {prog} \
{
	global tcl_platform gc app

	set app $prog

	if {$tcl_platform(platform) == "windows"} {
		#set _d(fixedFont) {{Lucida Console} 9}
		#set _d(fixedBoldFont) {{Lucida Console} 9 bold}
		set _d(fixedFont) {6x13}
		set _d(fixedBoldFont) {6x13bold}

		#set _d(fixedBoldFont) {helvetica 9 roman bold}
		#set _d(fixedFont -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1
		#set _d(fixedBoldFont -misc-fixed-bold-r-semicondensed--13-120-75-75-c-60-iso8859-1
		set _d(buttonFont) {helvetica 9 roman bold}
		set _d(cset.leftWidth) 40
		set _d(cset.rightWidth) 80
		set _d(scrollWidth) 18		;# scrollbar width
		set _d(help.scrollWidth) 20	;# helptool scrollbar width
		set _d(fm.activeOldFont) {{Lucida Console} 9 bold}
		set _d(fm.activeNewFont) {{Lucida Console} 9 bold}
		set _d(ci.filesHeight) 10
	} else {
		set _d(fixedFont) {6x13}
		set _d(fixedBoldFont) {6x13bold}
		set _d(buttonFont) {times 12 roman bold}
		set _d(cset.leftWidth) 55
		set _d(cset.rightWidth) 80
		set _d(scrollWidth) 12		;# scrollbar width
		set _d(help.scrollWidth) 14	;# helptool scrollbar width
		set _d(fm.activeOldFont) {6x13bold}
		set _d(fm.activeNewFont) {6x13bold}
		set _d(ci.filesHeight) 9	;# num files to show in top win
		set _d(fm.editor) "fm2tool"
	}

	if {$tcl_platform(platform) == "windows"} {
		set _d(buttonColor) #d4d0c8	;# menu buttons
		set _d(BG) #d4d0c8		;# default background
	} else {
		set _d(buttonColor) #d0d0d0	;# menu buttons
		set _d(BG) #d9d9d9		;# default background
	}

	set _d(backup) ""		;# Make backups in ciedit: XXX NOTDOC 
	set _d(balloonTime) 1000	;# XXX: NOTDOC
	set _d(buttonColor) #d0d0d0	;# menu buttons
	set _d(diffHeight) 30		;# height of a diff window
	set _d(diffWidth) 65		;# width of side by side diffs
	set _d(geometry) ""		;# default size/location
	set _d(listBG) #e8e8e8		;# topics / lists background
	set _d(mergeHeight) 24		;# height of a merge window
	set _d(mergeWidth) 80		;# width of a merge window
	set _d(newColor) lightblue     	;# color of new revision/diff
	set _d(noticeColor) #b0b0e0	;# messages, warnings
	set _d(oldColor) #d070ff     	;# color of old revision/diff
	set _d(mergeColor) lightblue	;# color of merge region
	set _d(searchColor) yellow	;# highlight for search matches
	set _d(selectColor) lightblue	;# current file/item/topic
	set _d(statusColor) lightblue	;# various status windows
	#XXX: Not documented yet
	set _d(infoColor) powderblue	;# color of info line in difflib
	set _d(textBG) white		;# text background
	set _d(textFG) black		;# text color
	set _d(scrollColor) #d9d9d9	;# scrollbar bars
	set _d(troughColor) lightblue	;# scrollbar troughs
	set _d(warnColor) yellow	;# error messages

	set _d(quit)	Control-q	;# binding to exit tool

	set _d(ci.editHeight) 30	;# editor height
	set _d(ci.editWidth) 80		;# editor width
	set _d(ci.excludeColor) red	;# color of the exclude X
	set _d(ci.editor) ciedit	;# editor: ciedit=builtin, else in xterm
	set _d(ci.display_bytes) 8192	;# number of bytes to show in new files
	set _d(ci.commentsHeight) 6	;# height of comment window
	set _d(ci.diffHeight) 30	;# number of lines in the diff window
	set _d(ci.rescan) 0		;# Do a second scan to see if anything
					;# changed. Values 0 - off 1 - on

	set _d(cset.listHeight) 12

	set _d(diff.diffHeight) 50
	set _d(diff.searchColor) lightblue ;# highlight for search matches

	# fmtool fonts: See operating specific section above
	set _d(fm.activeLeftColor) orange  ;# Color of active left region
	set _d(fm.activeRightColor) yellow ;# Color of active right region
	set _d(fm3.comments) 1		;# show comments window
	set _d(fm3.annotate) 1		;# show annotations
	set _d(fm3.firstdiff) -
	set _d(fm3.lastdiff) +
	set _d(fm3.nextdiff) bracketright
	set _d(fm3.prevdiff) bracketleft
	set _d(fm3.nextconflict) braceright
	set _d(fm3.prevconflict) braceleft
	set _d(fm3.undo) u

	set _d(help.linkColor) blue	;# hyperlinks
	set _d(help.topicsColor) orange	;# highlight for topic search matches
	set _d(help.height) 50		;# number of rows to display
	set _d(help.width) 72		;# number of columns to display
	set _d(help.helptext) ""	;# -f<helptextfile> - undocumented
	set _d(help.exact) 0		;# helpsearch, allows partial matches

	set _d(rename.listHeight) 8

	set _d(rev.canvasBG) #9fb6b8	  ;# graph background
	set _d(rev.commentBG) lightblue   ;# background of comment text
	set _d(rev.arrowColor) darkblue   ;# arrow color
	set _d(rev.mergeOutline) darkblue ;# merge rev outlines
	set _d(rev.revOutline) darkblue   ;# regular rev outlines
	set _d(rev.revColor) #9fb6b8	  ;# unselected box fills
	set _d(rev.localColor) green	  ;# local node (for resolve)
	set _d(rev.remoteColor) red	  ;# remote node (for resolve)
	set _d(rev.tagColor) red	  ;# tag box fills
	set _d(rev.selectColor) #adb8f6   ;# highlight color for selected tag
	set _d(rev.dateColor) #181818	  ;# dates at the bottom of graph
	set _d(rev.commentHeight) 5       ;# height of comment text widget
	set _d(rev.textWidth) 92	  ;# width of text windows
	set _d(rev.textHeight) 30	  ;# height of lower window
	set _d(rev.showHistory) "1M"	  ;# History to show in graph on start
	set _d(rev.showRevs) 50		  ;# Num of revs to show in graph 
	# XXX: not documented yet
	set _d(rev.savehistory) 5	  ;# Max # of files to save in file list
	set _d(rev.hlineColor) white	  ;# Color of highlight lines XXX:NOTDOC
	set _d(rev.sccscat) "-aum"	  ;# Options given to sccscat

	set _d(setup.mandatoryColor) #deeaf4 ;# Color of mandatory fields

	set _d(bug.mandatoryColor) #deeaf4 ;# Color of mandatory fields
	set _d(entryColor) white	   ;# Color of mandatory fields

	if {$tcl_platform(platform) == "windows"} {
		package require registry
		set gc(appdir) [registry get {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders} AppData]
		set gc(bkdir) [file join $gc(appdir) BitKeeper]
		if {![file isdirectory $gc(bkdir)]} { file mkdir $gc(bkdir) }
		set rcfile [file join $gc(bkdir) _bkgui]
	} else {
		set rcfile "~/.bkgui"
		set gc(bkdir) "~"
	}
	if {[file readable $rcfile]} { source $rcfile }

	# Pass one just copies all the defaults into gc unless they are set
	# already by .bkgui rcfile.
	foreach index [array names _d] {
		if {! [info exists gc($index)]} {
			set gc($index) $_d($index)
			#puts "gc\($index) = $_d($index) (default)"
		}
	}

	# Pass to converts from global field to prog.field
	foreach index [array names gc] {
		if {[string first "." $index] == -1} {
			set i "$prog.$index"
			if {![info exists gc($i)]} {
				set gc($i) $gc($index)
				#puts "gc\($i) = $gc($i) from $index"
			}
		}
    	}
}

# Try to find the project root, limiting ourselves to 40 directories
proc cd2root { {startpath {}} } \
{
	set n 40
	if {$startpath != ""} {
		set dir $startpath
	} else {
		set dir "."
	}
	while {$n > 0} {
		set path [file join $dir BitKeeper etc]
		if {[file isdirectory $path]} {
			cd $dir
			return
		}
		set dir [file join $dir ..]
		incr n -1
	}
	return -1
}

proc displayMessage {msg {exit {}}} \
{
	global tcl_platform

	if {$exit != ""} {
		set title "Error"
		set icon "error"
	} else {
		set title "Info"
		set icon "info"
	}
	tk_messageBox -title $title -type ok -icon $icon -message $msg
	if {$exit == 1} {
		exit 1
	} else {
		return
	}
}

# The balloon stuff was taken from the tcl wiki pages and modified by
# ask so that it can take a command pipe to display
proc balloon_help {w msg {cmd {}}} {

	global gc app

	set tmp ""
	if {$cmd != ""} {
		set msg ""
		set fid [open "|$cmd" r]
		while {[gets $fid tmp] >= 0} {
		#	lappend msg $tmp
			set msg "$msg\n$tmp"
		}
	}
	bind $w <Enter> \
	    "after $gc($app.balloonTime) \"balloon_aux %W [list $msg]\""
	bind $w <Leave> \
	    "after cancel \"balloon_aux %W [list $msg]\"
	    after 100 {catch {destroy .balloon_help}}"
    }

proc balloon_aux {w msg} {
	set t .balloon_help
	catch {destroy $t}
	toplevel $t
	wm overrideredirect $t 1
	label $t.l \
	    -text $msg \
	    -relief solid \
	    -padx 5 -pady 2 \
	    -borderwidth 1 \
	    -justify left \
	    -background lightyellow 
	pack $t.l -fill both
	set x [expr [winfo rootx $w]+6+[winfo width $w]/2]
	set y [expr [winfo rooty $w]+6+[winfo height $w]/2]
	wm geometry $t +$x\+$y
	bind $t <Enter> {after cancel {catch {destroy .balloon_help}}}
	bind $t <Leave> "catch {destroy .balloon_help}"
}

#
# Tcl version 8.0.5 doesn't have array unset 
# Consider moving to common lib area?
#
proc array_unset {var} \
{
	upvar #0 $var lvar

	foreach i [array names lvar] {
		#puts "unsetting $var ($i)"
		unset lvar($i)

	}
}

# From a Cameron Laird post on usenet
proc print_stacktrace {} {
	set depth [info level]
	puts "Current call stack shows"
	for {set i 1} {$i < $depth} {incr i} {
		puts "\t[info level $i]"
	}
}
proc _parray {a {pattern *}} {
	upvar 1 $a array
	if {![array exists array]} {
		error "\"$a\" isn't an array"
	}
	set maxl 0
	foreach name [lsort [array names array $pattern]] {
		if {[string length $name] > $maxl} {
			set maxl [string length $name]
		}
	}
	set maxl [expr {$maxl + [string length $a] + 2}]
	set answer ""
	foreach name [lsort [array names array $pattern]] {
		set nameString [format %s(%s) $a $name]
		append answer \
		    [format "%-*s = %s\n" $maxl $nameString $array($name)]
	}
	return $answer
}
proc progress_create {title lines} \
{
	global	progress_exit

	set progress_exit 0
	if {[catch {toplevel .f}] != 0} { return }
	.f configure -borderwidth 10 
	    frame .f.g -borderwidth 3 -relief sunken 
		canvas .f.g.c -borderwidth 0 -background white \
		    -highlightthickness 0 \
		    -width 230 -height 20
		pack .f.g.c -expand yes
		.f.g.c create rectangle 0 0 0 20 -outline "" -fill #a0a0ff \
		    -tags bar
		.f.g.c create text 115 10 -anchor c -text "0%" -tags value
	    frame .f.status
		for {set i 0} {$i < $lines} {incr i} {
			label .f.status.$i \
			    -font {fixed 12 roman bold} -borderwid 1 
			pack .f.status.$i -side top -expand yes
		}
	    frame .f.b -borderwidth 2
		button .f.b.b -text "Cancel" -command { progress_done }
		pack .f.b.b -fill x
	    pack .f.g -side top -fill both
	    pack .f.status -side top -fill both
	    pack .f.b -side top -fill both
	.f configure -cursor left_ptr

	set x [expr {[winfo screenwidth .] / 2}]
	incr x -105
	set y [expr {[winfo screenheight .] / 2}]
	incr y -200
	wm title .f "$title"
        wm geometry .f "+$x+$y"
	wm withdraw .
	update
}

proc progress {done data} \
{
	if {$done < 0 || $done > 100} { return }
	set msg [format "%3.0f%%" $done]
	.f.g.c itemconfigure value -text $msg
	set w [expr {0.01 * $done * [winfo width .f.g.c]}]
	set h [winfo height .f.g.c]
	.f.g.c coords bar 0 0 $w $h
	for {set i 0} {$i < [llength $data]} {incr i} {
		.f.status.$i configure -text [lindex $data $i]
	}
}

proc progress_done {} \
{
	global progress_exit

	set progress_exit 1
}

proc progress_destroy {} \
{
	catch { destroy .f } junk
}

proc progress_test {} \
{
	progress_create 0
	for {set i 0} {$i <= 10000} {incr i} {
		set x [expr {$i / 100}]
		progress $x {}
	}
	after 500
	progress_done
}
#
# This search library code can be called from other bk tcl/tk applications
#
# To add the search feature to a new app, you need to add the following
# lines:
#
# search_widgets .enclosing_frame .widget_to_search
# search_keyboard_bindings
#
# The search_widgets procedure takes two arguments. The first argument
# is the enclosing widget that the search buttons and prompts will be
# packed into. The second argument is the widget that search will do
# its searching in.
# 

proc searchbuttons {button state} \
{
	global	search

	if {$button == "both"} {
		if {[info exists search(next)]} {
			$search(next) configure -state $state
		}
		if {[info exists search(prev)]} {
			$search(prev) configure -state $state
		}
	} elseif {$button == "prev"} { 
		if {[info exists search(prev)]} {
			$search(prev) configure -state $state
		}
	} else {
		if {[info exists search(next)]} {
			$search(next) configure -state $state
		}
	}
}

proc searchdir {dir} \
{
	global	search

	set search(dir) $dir
}

proc search {dir} \
{
	global	search

	searchreset
	set search(dir) $dir
	if {$dir == ":"} {
		$search(menu) configure -text "Goto Line"
		set search(prompt) "Goto Line:"

	} elseif {$dir == "g"} {
		$search(menu) configure -text "Goto Diff"
		set search(prompt) "Goto diff:"
	} else {
		$search(menu) configure -text "Search Text"
		set search(prompt) "Search for:"
	}
	focus $search(text)
	searchbuttons both disabled
}

proc searchreset {} \
{
	global	search

	set string [$search(text) get]
	if {"$string" != ""} {
		set search(lastsearch) $string
		set search(lastlocation) $search(start)
		$search(text) delete 0 end
		if {[info exists search(clear)]} {
			$search(clear) configure -state disabled
		}
		if {[info exists search(recall)] && "$string" != ""} {
			$search(recall) configure -state normal \
			    -text "Recall search"
		}
	}
	if {$search(dir) == "?"} {
		set search(start) "end"
	} else {
		set search(start) "1.0"
	}
	searchbuttons both disabled
	set search(where) $search(start)
	if {[info exists search(status)]} {
		$search(status) configure -text ""
	}
}

proc searchrecall {} \
{
	global	search

	if {[info exists search(lastsearch)]} {
		$search(text) delete 0 end
		$search(text) insert end $search(lastsearch)
		set search(start) $search(lastlocation)
		searchsee $search(lastlocation)
		if {[info exists search(recall)]} {
			$search(recall) configure -state disabled
		}
		if {[info exists search(clear)]} {
			$search(clear) configure -state normal \
			    -text "Clear search"
		}
		searchbuttons both normal
	}
}

proc searchactive {} \
{
	global	search

	set string [$search(text) get]
	if {"$string" != ""} { return 1 }
	return 0
}

proc searchstring {} \
{
	global	search lastDiff

	if {[info exists search(focus)]} { 
		focus $search(focus) 
	}
	# One would think that [0-9][0-9]* would be the more appropriate
	# regex to find an integer... -ask
	set string [$search(text) get]
	if {"$string" == ""} {
		searchreset
		return
	} elseif {("$string" != "") && ($search(dir) == ":")} {
		if {[string match {[0-9]*} $string]} {
		    $search(widget) see "$string.0"
		} elseif {[string match {[0-9]*} $string] || 
		    ($string == "end") || ($string == "last")} {
			$search(widget) see end
		} else {
			$search(status) configure -text "$string not integer"
		}
		return
	} elseif {("$string" != "") && ($search(dir) == "g")} {
		if {[string match {[0-9]*} $string]} {
			catch {$search(widget) see diff-${string}}
			set lastDiff $string
			#set n [$search(widget) mark names]
			#set l [$search(widget) index diff-${string}]
			#displayMessage "l=($l) trying mark=(diff-${string})"
			if {[info procs dot] != ""} { dot }
			return
		} else {
			$search(status) configure -text "$string not integer"
			return
		}
	} else {
		set search(string) $string
		if {[info exists search(clear)]} {
			$search(clear) configure -state normal \
			    -text "Clear search"
		}
	}
	if {[searchnext] == 0} {
		searchreset
		if {[info exists search(status)]} {
			$search(status) configure -text "$string not found"
		}
	} else {
		if {[info exists search(status)]} {
			$search(status) configure -text ""
		}
	}
}

proc searchnext {} \
{
	global	search

	if {![info exists search(string)]} {return}

	if {$search(dir) == "/"} {
		set w [$search(widget) \
		    search -regexp -count l -- \
		    $search(string) $search(start) "end"]
	} else {
		set i ""
		catch { set i [$search(widget) index search.first] }
		if {"$i" != ""} { set search(start) $i }
		set w [$search(widget) \
		    search -regexp -backwards -count l -- \
		    $search(string) $search(start) "1.0"]
	}
	if {"$w" == ""} {
		if {[info exists search(focus)]} { focus $search(focus) }
		if {$search(dir) == "/"} {
			searchbuttons next disabled
		} else {
			searchbuttons prev disabled
		}
		return 0
	}
	searchbuttons both normal
	searchsee $w
	set search(start) [$search(widget) index "$w + $l chars"]
	$search(widget) tag remove search 1.0 end
	$search(widget) tag add search $w "$w + $l chars"
	$search(widget) tag raise search
	if {[info exists search(focus)]} { focus $search(focus) }
	return 1
}

proc gotoLine {} \
{
	global search

	set location ""

	$search(widget) index $location
	searchsee $location
	exit
}

# Default widget scroller, overridden by tools such as difftool
proc searchsee {location} \
{
	global	search

	$search(widget) see $location
}

proc clearOrRecall {} \
{
	global search 

	set which [$search(clear) cget -text]
	if {$which == "Recall search"} {
		searchrecall
	} else {
		searchreset
	}
}

proc search_keyboard_bindings {{nc {}}} \
{
	global search

	if {$nc == ""} {
		bind .                <g>             "search g"
		bind .                <colon>         "search :"
		bind .                <slash>         "search /"
		bind .                <question>      "search ?"
	}
	bind .                <Control-u>     searchreset
	bind .                <Control-r>     searchrecall
	bind $search(text)      <Return>        searchstring
	bind $search(text)      <Control-u>     searchreset
	# In the search window, don't listen to "all" tags.
        bindtags $search(text) [list $search(text) Entry]
}

proc search_init {w s} \
{
	global search app gc

	set search(prompt) "Search for:"
	set search(plabel) $w.prompt
	set search(dir) "/"
	set search(text) $w.search
	set search(menu) $w.smb
	set search(widget) $s
	set search(next) $w.searchNext
	set search(prev) $w.searchPrev
	set search(focus) .
	set search(clear) $w.searchClear
	set search(recall) $w.searchClear
	set search(status) $w.info
}

proc search_widgets {w s} \
{
	global search app gc

	search_init $w $s

	image create photo prevImage \
	    -format gif -data {
R0lGODdhDQAQAPEAAL+/v5rc82OkzwBUeSwAAAAADQAQAAACLYQPgWuhfIJ4UE6YhHb8WQ1u
WUg65BkMZwmoq9i+l+EKw30LiEtBau8DQnSIAgA7
}
	image create photo nextImage \
	    -format gif -data {
R0lGODdhDQAQAPEAAL+/v5rc82OkzwBUeSwAAAAADQAQAAACLYQdpxu5LNxDIqqGQ7V0e659
XhKKW2N6Q2kOAPu5gDDU9SY/Ya7T0xHgTQSTAgA7
}
	label $search(plabel) -font $gc($app.buttonFont) -width 11 \
	    -relief flat \
	    -textvariable search(prompt)

	# XXX: Make into a pulldown-menu! like is sccstool
	menubutton $search(menu) -font $gc($app.buttonFont) -relief raised \
	    -bg $gc($app.buttonColor) -pady $gc(py) -padx $gc(px) \
	    -borderwid $gc(bw) \
	    -text "Search" -width 15 -state normal \
	    -menu $search(menu).menu
	    set m [menu $search(menu).menu]
	    $m add command -label "Search text" -command {
		$search(menu) configure -text "Search text"
		search /
		# XXX
	    }
	    $m add command -label "Goto Diff" -command {
		$search(menu) configure -text "Goto Diff"
		search g
		# XXX
	    }
	    $m add command -label "Goto Line" -command {
		$search(menu) configure -text "Goto Line"
		search :
		# XXX
	    }
	entry $search(text) -width 20 -font $gc($app.buttonFont)
	button $search(prev) -font $gc($app.buttonFont) \
	    -bg $gc($app.buttonColor) \
	    -pady $gc(py) -padx $gc(px) -borderwid $gc(bw) \
	    -image prevImage \
	    -state disabled -command {
		    searchdir ?
		    searchnext
	    }
	button $search(next) -font $gc($app.buttonFont) \
	    -bg $gc($app.buttonColor) \
	    -pady $gc(py) -padx $gc(px) -borderwid $gc(bw) \
	    -image nextImage \
	    -state disabled -command {
		    searchdir /
		    searchnext
	    }
	button $search(clear) -font $gc($app.buttonFont) \
	    -bg $gc($app.buttonColor) \
	    -pady $gc(py) -padx $gc(px) -borderwid $gc(bw) -width 15\
	    -text "Clear search" -state disabled -command { clearOrRecall }
	label $search(status) -width 20 -font $gc($app.buttonFont) -relief flat

	pack $search(menu) -side left -expand 1 -fill y
	pack $search(text) -side left
	pack $search(prev) -side left -fill y
	pack $search(clear) -side left -fill y
	pack $search(next) -side left -fill y
	pack $search(status) -side left -expand 1 -fill x

	$search(widget) tag configure search \
	    -background $gc($app.searchColor) -font $gc($app.fixedBoldFont)
}

proc example_main_widgets {} \
{
	#global	search 

	set search(prompt) ""
	set search(dir) ""
	set search(text) .cmd.t
	set search(focus) .p.top.c
	set search(widget) .p.bottom.t

	frame .cmd -borderwidth 2 -relief ridge
		text $search(text) -height 1 -width 30 -font $font(button)
		label .cmd.l -font $font(button) -width 30 -relief groove \
		    -textvariable search(prompt)

	# Command window bindings.
	bind .p.top.c <slash> "search /"
	bind .p.top.c <question> "search ?"
	bind .p.top.c <n> "searchnext"
	bind $search(text) <Return> "searchstring"
	$search(widget) tag configure search \
	    -background yellow -relief groove -borderwid 0
}

# -------------------------- Editor module -----------------------------------
# ciedit - a tool for editing files during checkin.

proc eat {fd} \
{
	global	edit_busy

	if {[gets $fd buf] >= 0} {
		puts $buf
	} elseif {[eof $fd]} {
		set edit_busy 0
		close $fd
	}
}

proc cmd_edit {which} \
{
	global	curLine edit_busy gc filename w tmp_dir env

	# If comments are in the comments window, save them before invoking 
	# the editor
	set cmts [$w(c_comments) get 1.0 "end - 1 char"]
	if {$cmts != ""} {
		saveComments $filename $cmts
	}
	if {$edit_busy == 1} { return }
	set edit_busy 1
	if {[file writable $filename]} {
		if {$which == "gui"} {
			edit_widgets
			edit_file
		} elseif {$which == "fmtool"} {
			set old [file join $tmp_dir old[pid]]
			catch {exec bk get -qkp $filename >$old}
			if {![file readable $old] || [file size $old] == 0} {
				# XXX - replace with popup when I merge 
				exec bk msg "Unable to bk get $filename"
				set edit_busy 0
				return
			}
			set merge [file join $tmp_dir merge[pid]]
			set fd [open "| bk fmtool $old $filename $merge" r]
			fileevent $fd readable "eat $fd"
			vwait edit_busy
			if {[file readable $merge]} {
				catch {file rename -force $merge $filename}
			} 
			catch {file delete $old $merge}
		} else {
			if {[info exists env(EDITOR)]} {
				set editor $env(EDITOR)
			} else {
				set editor vim
			}
			set geom "$gc(ci.editWidth)x$gc(ci.editHeight)"
			set fd [open "| xterm -g $geom -e $editor $filename" r]
			fileevent $fd readable "eat $fd"
			vwait edit_busy
		}
	}
	cmd_refresh 1
}

proc edit_widgets {} \
{
	global	adjust nextMark firstEditConfg edit_changed
	global	tcl_platform gc

	# Defaults
	if {$tcl_platform(platform) == "windows"} {
		set y 0
		set x 2
		set filesHt 9
	} else {
		set y 2
		set x 2
		set filesHt 7
	}
	set wmEdit "+1+47"
	set firstEditConfg 1
	set nextMark 0
	set adjust 0
	set edit_changed ""

	toplevel .edit
	wm geometry .edit $wmEdit
	wm protocol .edit WM_DELETE_WINDOW edit_exit
	set halfScreen [expr {$gc(ci.editHeight) / 2}]

	frame .edit.status -borderwid 2
		label .edit.status.l -font $gc(ci.fixedFont) \
		    -wid 84 -relief groove
		grid .edit.status.l -sticky ew
	frame .edit.menus -borderwid 2 -relief groove
		set bwid 5
		button .edit.menus.help -width $bwid \
		    -pady $y -font $gc(ci.buttonFont) -text "Help" \
		    -command { exec bk helptool edittool & }
		button .edit.menus.quit -width $bwid \
		    -pady $y -font $gc(ci.buttonFont) -text "Quit" \
		    -command edit_exit
		button .edit.menus.save -width $bwid \
		    -pady $y -font $gc(ci.buttonFont) -text "Save" \
		    -command edit_save
		button .edit.menus.next -width 12 \
		    -pady $y -font $gc(ci.buttonFont) -text "Next change" \
		    -command {edit_next 1}
		button .edit.menus.prev -width 15 \
		    -pady $y -font $gc(ci.buttonFont) -text "Previous change" \
		    -command {edit_next -1}
		pack .edit.menus.prev -side left
		pack .edit.menus.next -side left
		pack .edit.menus.save -side left
		pack .edit.menus.help -side left
		pack .edit.menus.quit -side left
	frame .edit.t -borderwidth 2 -relief raised
		text .edit.t.t -spacing1 1 -spacing3 1 -wrap none \
		    -bg $gc(ci.textBG) -fg $gc(ci.textFG) \
		    -font $gc(ci.fixedFont) \
		    -width $gc(ci.editWidth) \
		    -height $gc(ci.editHeight) \
		    -xscrollcommand { .edit.t.x1scroll set } \
		    -yscrollcommand { .edit.t.y1scroll set }
		    scrollbar .edit.t.x1scroll -orient horiz \
			-width $gc(ci.scrollWidth) \
			-command ".edit.t.t xview" \
			-troughcolor $gc(ci.troughColor) \
			-background $gc(ci.scrollColor)
		    scrollbar .edit.t.y1scroll \
			-width $gc(ci.scrollWidth) \
			-command ".edit.t.t yview" \
			-troughcolor $gc(ci.troughColor) \
			-background $gc(ci.scrollColor)
		grid .edit.t.t -row 0 -column 0 -sticky nsew
		grid .edit.t.y1scroll -row 0 -column 1 -rowspan 2 -sticky ns
		grid .edit.t.x1scroll -row 1 -column 0 -sticky ews
	grid .edit.status -row 0 -sticky nsew
	grid .edit.menus -row 1 -sticky nsew
	grid .edit.t -row 2 -sticky nsew
	grid rowconfigure .edit.t 0 -weight 1
	grid rowconfigure .edit 2 -weight 1

	grid columnconfigure .edit.status 0 -weight 1
	grid columnconfigure .edit.menus 0 -weight 1
	grid columnconfigure .edit.t 0 -weight 1
	grid columnconfigure .edit 0 -weight 1

	.edit.menus.prev configure -state disabled
	focus .edit.t.t

	bind .edit.t.t <Control-d> {
		.edit.t.t yview scroll $halfScreen units
		break
	}
	bind .edit.t.t <Control-u> {
		.edit.t.t yview scroll -$halfScreen units
		break
	}
	bind .edit.t.t <Control-n> { edit_next 1; break }
	bind .edit.t.t <Shift-Next> { edit_next 1; break }
	bind .edit.t.t <Control-p> { edit_next -1; break }
	bind .edit.t.t <Shift-Prior> { edit_next -1; break }

	bind .edit.t.t <Configure> {
		global gc firstEditConfg

		set x [winfo height .edit.t.t]
		# This gets executed once, when we know how big the text is
		if {$firstEditConfg == 1} {
			set h [winfo height .edit.t.t]
			set pixelsPerLine [expr {$h / $gc(ci.editHeight)}]
			set firstEditConfg 0
		}
		set x [expr {$x / $pixelsPerLine}]
		set gc(ci.editHeight) $x
		set halfScreen [expr {$gc(ci.editHeight) / 2}]
	}
	bind .edit.t.t <Next> " .edit.t.t yview scroll 1 pages; break"
	bind .edit.t.t <Prior> ".edit.t.t yview scroll -1 pages; break"
	bind .edit.t.t <Home> ".edit.t.t yview -pickplace 1.0; break"
	bind .edit.t.t <End> ".edit.t.t yview -pickplace end; break"
	bind .edit.t.t <Control-f> " .edit.t.t yview scroll 1 pages; break"
	bind .edit.t.t <Control-b> ".edit.t.t yview scroll -1 pages; break"
	bind .edit.t.t <Control-y> ".edit.t.t yview scroll -1 units; break"
	bind .edit.t.t <Control-e> ".edit.t.t yview scroll 1 units; break"
	bind .edit.t.t <Escape> "edit_exit"
	bind .edit.t.t <Alt-s> "edit_save"
	bind .edit.t.t <Delete> {
		set c [.edit.t.t get insert]
		if {$c == "\n"} { edit_adjust -1 }
	}
	bind .edit.t.t <BackSpace> {
		set c [.edit.t.t get "insert - 1 char"]
		if {$c == "\n"} { edit_adjust -1 }
		edit_changed
	}
	bind .edit.t.t <Return> { edit_adjust 1 }
	bind .edit.t.t <Key> {
		if {"%A" != "{}"} { edit_changed }
	}

	.edit.t.t tag configure "new" -background $gc(ci.selectColor)
	.edit.t.t tag configure "current" -relief groove -borderwid 2
}

proc edit_changed {} \
{
	global	filename n nextMark edit_changed

	if {$edit_changed == ""} {
		set edit_changed "\[ modified ]"
	}
	.edit.status.l configure -text \
	    "\[ $filename ]$edit_changed on change $nextMark of $n"
}

proc edit_next {v} \
{
	global	filename n marks markEnds nextMark edit_changed

	if {($nextMark + $v <= 0) || ($nextMark + $v > $n)} { return }
	incr nextMark $v
	set m $marks($nextMark)
	set start $marks($nextMark)
	set stop $markEnds($nextMark)
	.edit.t.t yview $m.0
	.edit.t.t yview scroll -5 units
	.edit.t.t tag remove "current" 1.0 end
	.edit.t.t tag add "current" $start.0 "$stop.0 - 1 char"
	.edit.status.l configure -text \
	    "\[ $filename ]$edit_changed on change $nextMark of $n"
	.edit.t.t mark set insert "$start.0"
	if {$nextMark > 1} {
		.edit.menus.prev configure -state normal
	} else {
		.edit.menus.prev configure -state disabled
	}
	if {$nextMark < $n} {
		.edit.menus.next configure -state normal
	} else {
		.edit.menus.next configure -state disabled
	}
}

# This tries real hard to adjust the diff highlights after the user adds
# or deletes newlines.
proc edit_adjust {v} \
{
	global adjust

	incr adjust $v
	after idle {
		global	n marks markEnds

		set where [lindex [split [.edit.t.t index insert] .] 0]
		set i 1
		while {$i <= $n} {
			set new [expr {$marks($i) + $adjust}]
			if {$new > $where} {
			    #puts "where=$where incr start $marks($i) $adjust"
				incr marks($i) $adjust
			}
			set new [expr {$markEnds($i) + $adjust}]
			if {($new > $where) && ($markEnds($i) > $where)} {
			    #puts "where=$where incr stop $markEnds($i) $adjust"
				incr markEnds($i) $adjust
			}
			incr i 1
		}
		set adjust 0
		.edit.t.t tag remove "new" 1.0 end
		set i 1
		while {$i <= $n} {
			set start $marks($i).0
			set stop $markEnds($i).0
			.edit.t.t tag add "new"  $start $stop
			#puts "i=$i $marks($i) $markEnds($i)"
			incr i 1
		}
	}
}

proc edit_highlight {start stop} {
	global n markEnds marks
	.edit.t.t tag add "new" $start.0 "$stop.0 lineend"
	set marks($n) $start
	set markEnds($n) $stop
	#puts "n=$n $start $stop"
}

proc edit_file {} \
{
	global n filename sdiffw tmp_dir

	.edit.t.t configure -state normal
	.edit.t.t delete 1.0 end
	set old [file join $tmp_dir old[pid]]
	catch {exec bk get -qkp "$filename" > $old} err
	#displayMessage "$sdiffw ($old) ($filename) err=($err)"
	set d [open "| $sdiffw \"$old\" \"$filename\"" "r"]
	set f [open $filename "r"]
	set start 1
	set lineNo 1
	set n 0
	gets $d last
	gets $f data
	if {$last == "" || $last == " "} { set last "S" }
	while { [gets $d diff] >= 0 } {
		if {$diff == "" || $diff == " "} { set diff "S" }
		if {$diff != "<"} {
			.edit.t.t insert end "$data\n"
			set lastData $data
			gets $f data
			incr lineNo 1
		}
		if {$diff == "|"} { set diff ">" }
		if {$diff != $last} {
			switch $last {
			    # "S"	Don't care about sames.
			    # "<"	XXX - what do you do about deletes?
			    ">" { incr n 1; edit_highlight $start $lineNo }
			}
			set start $lineNo
			set last $diff
		}
	}
	catch {close $d} err
	catch {close $f} err
	.edit.t.t insert end "$data"
	switch $last {
	    # "S"	Don't care about sames.
	    # "<"	XXX - what do you do about deletes?
	    ">" { incr n 1; edit_highlight $start $lineNo }
	}
	edit_next 1
}

proc edit_save {} \
{
	global	edit_changed filename gc app

	if {[info exists gc($app.backup)] && ($gc($app.backup) != "")} {
		set backup [join [list $filename "bkp"] "~"]
		file copy -force $filename $backup
	}
	set d [open "$filename" "w"]
	puts -nonewline $d [.edit.t.t get 1.0 end]
	catch {close $d} err
	set edit_changed ""
	edit_exit
	cmd_refresh 1
}

proc confirm {what msg} \
{
	global w

	set ret [catch {toplevel .c}]
	if {$ret != 0} { return }

	frame .c.top
	    label .c.top.icon \
		-bitmap questhead
	    label .c.top.msg \
		-text $msg
	pack .c.top.icon -side left
	pack .c.top.msg -side right
	frame .c.sep \
		-height 2 \
		-borderwidth 1 \
		-relief sunken
	frame .c.controls
	    button .c.controls.ok \
		    -text "OK" \
		    -command $what
	    button .c.controls.cancel \
		    -text "cancel" \
		    -command "destroy .c"
	pack .c.controls.ok -side left -padx 4
	pack .c.controls.cancel -side right -padx 4
	pack .c.top -padx 8 -pady 8
	pack .c.sep -fill x -pady 4
	pack .c.controls -pady 4
	set x [expr {[winfo rootx .edit] + [winfo width $w(c_top)] - 250}]
	set y [expr {[winfo rooty .edit] + 120}]
	wm geometry .c "+$x+$y"
	wm title .c "Confirm Quit"
	wm transient .c $w(c_top)
}

# XXX - needs to figure out if we made any changes.
proc edit_exit {} \
{
	global	edit_busy edit_changed filename

	if {$edit_changed != ""} {
		confirm "edit_exit2" "Quit without saving $filename?"
	} else {
		edit_exit2
	}
}

proc edit_exit2 {} \
{
	global	edit_busy edit_changed

	set edit_busy 0
	destroy .edit
	if {$edit_changed != ""} {
		destroy .c
	}
}

# -------------------------- Editor done -----------------------------------
# citool - a tool for checking in SCCS files
# Copyright (c) 1998-2000 by Larry McVoy, Aaron Kushner; All rights reserved
#
# %W%

# Global Vars
#
# bmap:      [array] bitmaps for file-type icons
# stats:     [array] number of comments, files, pending files
# files:     [array] array of new and modified files from 'sfind'
# commit:     [array] list of files that will be part of the changeset. Used to
#                    select files to opt-in and opt-out. 
#                    "modified" files start in the selected state until the 
#                      user deselects them
#                    "pending" files can not be deselected
#                    "new" files start in non-added state unless there are 
#			   comments
# curLine:   [scalar]
# lastLine:  [scalar]
# status:    [array] keeps track whether a file is 'pending', 'new',
#     'modified', or 'csetFile'
# excluded:   [array]  keep track of whether the given file is in the exclude
#		      list or not
# w:         [array]  list of widgets

proc next {} \
{
	global  search

	if {[searchactive]} {
		set search(dir) "/"
		searchnext
		return
	}
	return
}

proc prev {} \
{
	global  search

	if {[searchactive]} {
		set search(dir) "?"
		searchnext
		return
	}
	return
}

# Display messages in the bottom message window
proc bottom {tag msg} \
{
	global w

	$w(c_lower).t configure -state normal
	$w(c_lower).t delete 1.0 end
	$w(c_lower).t insert end "\n"
	if {"$msg" != ""} {
		foreach cc [split $msg \n] {
			$w(c_lower).t insert end " $cc\n"
		}
		$w(c_lower).t tag add $tag 1.0 end
	}
	$w(c_lower).t tag remove "yell" insert end
	$w(c_lower).t insert end "\n"
	$w(c_lower).t configure -state disabled
	focus $w(c_comments)
}

proc ifBusy {} \
{
	global filename edit_busy

	if {$edit_busy != 0} {
		bottom "yell" \
"You need to finish editing $filename first.
Type Control-l to go back to diffs/history."
		return 1
	}
	return 0
}

# Call doNext if we are at the start of the comments and this key
# looks like it is going to add something.
proc top_changed {key} \
{
	global w

	set insert [$w(c_comments) index insert]
	set end [$w(c_comments) index "end - 1 char"]
	if {($insert == "1.0") && ($end == "1.0")} {
		after idle { doNext 0 }
	}
}

# This is just lame in the extreme.
proc checkEmpty {key} \
{
	global w

	set insert [$w(c_comments) index insert]
	set end [$w(c_comments) index "end - 1 char"]
	if {$key == "del"} {
		if {($end == "1.1") && ($insert == "1.0")} {
			after idle { doNext 0 }
		}
	} else {
		if {($end == "1.1") && ($insert == "1.1")} {
			after idle { doNext 0 }
		}
	}
}

# Erase the word before the cursor
proc cmd_werase {} \
{
	global w

	set insert [$w(c_comments) index insert]
	set i $insert
	set c [$w(c_comments) get $i]
	while {[string is space $c] && $i >= 1.1} {
		set i [$w(c_comments) index "$i - 1 chars"]
		set c [$w(c_comments) get $i]
	}
	while {![string is space $c] && $i >= 1.1} {
		set i [$w(c_comments) index "$i - 1 chars"]
		set c [$w(c_comments) get $i]
	}
	if {[string is space $c]} {
		set i [$w(c_comments) index "$i + 1 chars"]
	}
	$w(c_comments) delete $i $insert
	after idle { doNext 0 }
}

# Erase the current line
proc cmd_lerase {} \
{
	global w

	set insert [$w(c_comments) index insert]
	set start [$w(c_comments) index "insert linestart"]
	$w(c_comments) delete $start $insert
	after idle { doNext 0 }
}

proc doLast {} \
{
	global curLine lastLine w

	set last [$w(c_files) index "end - 1 char linestart"]
	if {"$last" == "$curLine"} { return }
	if {[ifBusy]} { return }
	set lastLine $curLine
	set curLine $last
	doSelect
}

# Returns the file name for a specific line. line must be in the form
# 1.0, 2.0, etc.
# XXX: Verify input -- is it in the form of number.0 ?
proc getName {line} \
{
	global w

	if {$line == 0} {puts "Error: (getName) line=($line)"; return }
	return [$w(c_files) get "$line + 4 char" "$line lineend"]
}

# Select all new files to be part of the cset
proc select_all_new {} \
{
	global stats status curLine lastLine filename state files w

	set c 0
	set oldstate $state(toggle_all)
	set numLines [$w(c_files) index "end -1 chars linestart" ]
	while {$c < $numLines} {
		set curLine [$w(c_files) index "1.0 + $c lines"]
		incr c
		set name [getName $curLine]
		if {$status($name) != "new"} {
			continue
		}
		set state(clicked) "true"
		doSelect
	}
	if {$oldstate == "true"} {
		set state(toggle_all) "false"
	} elseif {$oldstate == "false"} {
		set state(toggle_all) "true"
	}
}

#
# toggles new files between added/non-added state and modified files are
# toggled between exclude/include state 
#
proc toggle_state {} \
{
	global state

	if {$state(clicked) != "true"} {
		set state(clicked) "true"
	} else {
		set state(clicked) "false"
	}
	set state(oldclicked) $state(clicked)
	doNext 0
}

# doNext -1 Work on the previous file in the list
# doNext 0  Work on file on the current line
# doNext 1  Work on the next non-checked in file
#
proc doNext {x} \
{
	global curLine lastLine stats file_rev filename w

	if {[ifBusy]} { return }
	set lastLine $curLine
	if {$x == 0} {
		doSelect
		return
	}
	set numLines [$w(c_files) index "end -1 chars linestart" ]
	#puts "in doNext x=($x) curLine=($curLine) lastLine=($lastLine)"
	if {(("$x" == "-1") && ("$curLine" == "1.0")) ||
	    (("$x" == "1") && ("$curLine" >= $numLines))} {
	    	#puts "doNext return triggered curLine=($curLine)"
		return
	}
	if {$x > 0} { set inc 1 } else { set inc -1 }
	set c $curLine
	# Clear comments before jumping to next file
	#$w(c_comments) configure -state normal
	#$w(c_comments) delete 1.0 end
	#$w(c_comments) configure -state disabled
	# Now skip over checked in (pending) files
	while {(($c + $x) <= $numLines) && (($c + $x) >= 0)} {
		set lastLine $curLine
		set curLine [$w(c_files) index "$c + $x lines"]
		set tmp [getName $curLine]
		if {[regexp $file_rev $tmp dummy file rev]} {
			incr x $inc
		} else {
			doSelect
			return
		}
	}
}

# Sets the curLine global and also finds out whether the file-type icon 
# has been clicked
# By default, bound to mouse-button-1
proc doPixSelect {x y} \
{
	global curLine lastLine state w

	if {[ifBusy]} { return }
	# procuedure doNext mucks with the curLine global so we need to store
	# the old state and assign it back to curLine when exiting this proc
	set icurLine [$w(c_files) index "@$x,$y linestart"]

	# Process the line we were last at
	set lastLine $curLine
	# XXX: This doSelect seems to cause diff to be called twice.
	#doSelect

	set ws [$w(c_files) index "@$x,$y wordstart"]
	set we [$w(c_files) index "@$x,$y wordend"]
	set cs [lindex [split $ws . ] 1 ]
	set ce [lindex [split $we . ] 1 ]
	set ls [lindex [split $ws . ] 0 ]
	set le [lindex [split $we . ] 0 ]
	if {($cs >= 0) && ($ce <= 4) && ($ls == $le)} { 
		set state(clicked) "true" 
	} else {
		set state(clicked) "false"
	}
	$w(c_files) tag remove sel 1.0 end
	set curLine $icurLine

	# Now do the current line we clicked on
	doSelect
}

# Change the file-type icon at the start of a line
proc changeBitmap {line bm} \
{
	global bmap icons filename w

	if {$line == 0} {return}
	# Map the state names to the icon names
	switch $bm {
	    pending	{set bm "done"}
	    file	{set bm "modified"}
	}
	if {[lsearch [array names bmap] $bm] == -1} {
		puts "Error: invalid bitmap chosen bitmap=($bm)"
		return
	}
	#puts "changeBitmap changing icon to bm=($bm) for line=($line)"
	$w(c_files) configure -state normal
	$w(c_files) delete "$line + 1 char"
	$w(c_files) image create "$line + 1 char" -image $bmap($bm)
	$w(c_files) configure -state disabled
	# keep track of non-excluded bitmap (i.e. the original bitmap)
	if { $bm != "exclude" } {
		set icons($filename) $bm
	}
}

proc numFiles {} \
{
	global stats

	set all [expr {$stats(pending,f) + $stats(modified,f) + \
	    $stats(csetFile,f) + $stats(new,f)}]
	return $all
}

# Display contents of a file in the bottom widget
proc showFile {file bytes} \
{
	global w

	if {$file == ""} {return}
	if {![file exists $file]} {
		puts "Removing non-existent file \"$file\" from list box"
		removeFile $file
		return
	}
	$w(c_lower).t configure -state normal
	if {[file type $file] == "link"} {
		$w(c_lower).t insert insert \
		    "$file:\t(new file) type: [file type $file]\n\n"
    	} elseif {[file type $file] == "file"} {
		catch {file stat $file info} err
		$w(c_lower).t insert insert \
		    "$file:\t(new file) $info(size) bytes\n\n"
		set fd [open "| bk _strings $file" "r"]
		if {$bytes == 0} {
			set msg [read $fd]
		} else {
			set msg [read $fd $bytes]
		}
		$w(c_lower).t insert end "$msg\n" {""}
		catch {close $fd}
		$w(c_lower).t tag remove "yell" insert end
	} else {
		$w(c_lower).t insert insert \
		    "$file:\t UNSUPPORTED FILE TYPE ([file type $file])"
	}
	focus $w(c_comments)
	$w(c_lower).t configure -state disabled
}

# ak's debugging stuff
proc s_trace {v i o} \
{ 
	upvar 1 $v var
	puts "op=$o i=($i) v=$var($i)"
}

proc updateButtons {fname} \
{
	global status resolve w

	#puts "fname=($fname) status=($status($fname))"
	if {[info exists status($fname)]} {
		if {$status($fname) == "new"} {
			$w(c_menu).edit configure -state normal
			$w(c_menu).unedit configure -state normal
			$w(c_menu).difftool configure -state disabled
			$w(c_menu).history configure -text "Ignore" \
			    -command cmd_ignore
		} elseif {$status($fname) == "ignore"} {
			$w(c_menu).edit configure -state disabled
			$w(c_menu).unedit configure -state disabled
			$w(c_menu).difftool configure -state normal
			$w(c_menu).history configure -text "Unignore" \
			    -command cmd_ignore
		} elseif {$status($fname) == "pending"} {
			$w(c_menu).edit configure -state disabled
			$w(c_menu).unedit configure -state disabled
			$w(c_menu).difftool configure -state disabled
			$w(c_menu).history configure -text "History" \
			    -command cmd_history
		} elseif {$status($fname) == "modified"} {
			$w(c_menu).edit configure -state normal
			# If we are in the resolver DON'T unedit files
			if {$resolve != ""} {
				$w(c_menu).unedit configure -state disabled
			} else {
				$w(c_menu).unedit configure -state normal
			}
			$w(c_menu).difftool configure -state normal
			$w(c_menu).history configure -text "History" \
			    -command cmd_history
		} elseif {$status($fname) == "csetFile"} {
			$w(c_menu).edit configure -state disabled
			$w(c_menu).unedit configure -state disabled
			$w(c_menu).difftool configure -state disabled
			$w(c_menu).history configure -text "History" \
			    -command cmd_history
		}
	}
} ;# updateButtons

# Starts working on previous line and when done with processing prev
# line, makes the new line the selected line.
#
# Figures out if comments have been added or deleted from the comment
#     window and saves or deletes the comment from the comment array
# Sets the 'filename' var if we are on a valid file
#
proc doSelect {} \
{
	global	filename saved curLine lastLine csetFile msg
	global	pwd stats bmap file_rev commit status 
	global  icons state gc excluded w
	
	# Automatically scroll the file window (file is third from the top)
	set top [expr {$curLine - 3}]
	set numLines [$w(c_files) index "end -1 chars linestart" ]
	if {$top >= 0} {
		set frac [expr {$top / $numLines}]
		$w(c_files) yview moveto $frac
	}
	set tfname [getName $curLine]
	updateButtons $tfname

	# Check to see if we are dealing with a mouse click event on the
	# icon of new file. If so, add or delete it from the cset
	if {($state(clicked) == "true") &&
	    [info exists status($tfname)] && ($status($tfname) == "new")} {
		# OK, this is a new file so we set the filename variable
		# so the rest of doSelect can work with this file
		set filename $tfname
		if {($commit($filename) == 0) || 
		    ($state(toggle_all) == "true")} {
			#puts "($filename) non-added -> added"
			$w(c_comments) configure -state normal
			$w(c_comments) delete 1.0 end
			$w(c_comments) insert end \
			    "New BitKeeper file ``$filename''"
			$w(c_comments) configure -state disabled
			if {$state(toggle_all) != "true"} {
				incr stats($status($filename),c)
			}
			changeBitmap $curLine "done"
			set commit($filename) 1
			bottom "" ""
			showFile $filename $gc(ci.display_bytes)
		} elseif {($commit($filename) == 1) && 
		    ($state(toggle_all) != "true")} {
			#puts "($filename) added -> non-added"
			changeBitmap $curLine "new"
			$w(c_comments) configure -state normal
			$w(c_comments) delete 1.0 end
			$w(c_comments) configure -state disabled
			if { $status($filename) == "new" } {
				bottom "yell" "$msg(nonrc)"
				showFile $filename $gc(ci.display_bytes)
			}
			set commit($filename) 0
			# Don't decrement stats here -- it is done below
			# when the comment is deleted
			# (incr stats($status($filename)) -1)
			#puts "decrementing stats value=($stats(new)"
		}
		set lastLine $curLine
	}
	# Perform include and exclude operations on the selected file
	if {($state(clicked) == "true") && ($tfname != $csetFile) && \
	    [info exists status($tfname)] &&
	    (($status($tfname) == "pending") || 
	    ($status($tfname) == "modified"))} {
		#set filename $tfname
		if {$excluded($tfname) == 1} {
			#puts "exc ($tfname) excluded -> included"
			includeFile $curLine $tfname
		} elseif {$excluded($tfname) == 0} {
			#puts "exc ($tfname) included -> excluded"
			excludeFile $curLine $tfname
		}
	}
	$w(c_comments) configure -state normal
	# If this is the first time through, filename hasn't been set
	# yet, so set it to the filename on our current line
	# XXX: THIS CAUSES A BUG where the cset comments gets deleted when
	# discarding a new or modified file and then control-n'ing to the
	# cset file.
	if {($filename == "") && ($tfname == "$csetFile")} {
		set filename $tfname
		##puts "setting filename=($filename)"
	}
	clearCheckin

	# Get the comments from the screen and save them if they are non-saved.
	# And don't muck with pending files -- their comments don't change!
	set c_screen [$w(c_comments) get 1.0 "end - 1 char"]
	set c_file [readComments $filename]
	#if {$filename == "$csetFile"} {saveComments $filename $c_screen}
	if {($filename != "") && ($status($filename) != "pending")} {
		if {"$c_screen" != ""} {
			if {(![commentfileExists $filename]) && 
			    ($status($filename) != "new")} {
				incr stats($status($filename),c)
			}
			saveComments $filename $c_screen
			if {$excluded($filename)} {
				set icons($filename) "exclude"
			} else {
				set icons($filename) "done"
			}
			if {$filename == $csetFile} {
				$w(c_menu).checkin configure -text "Commit"
			}
			changeBitmap $lastLine $icons($filename)
			set commit($filename) 1
		} elseif {"$c_file" != ""} {
			deleteCommentfile $filename
			if {$stats($status($filename),c) >= 0} {
				incr stats($status($filename),c) -1 
			}
			if {$filename == $csetFile} {
				set icons($filename) "cset"
				$w(c_menu).checkin configure -text "Checkin"
			} elseif {($status($filename) == "new")} {
				set icons($filename) "new"
			} else {
				set icons($filename) "modified"
			}
			changeBitmap $lastLine $icons($filename)
			# Update cset icon if no comments left
			# Need to fix this so that when any files have comments,
			# the correct bitmap is put back
			#if {($stats(new,c) == 0) && 
			#    ($stats(modified,c) == 0)} {
			#	set numLines \
			#	    [$w(c_files) index "end -1 chars linestart" ]
			#	changeBitmap $numLines "cset"
			#}
			set commit($filename) 0
		}
	}
	# Exclude the ChangeSet file and new files in the number commented
	set nF [expr {$stats(pending,f) + $stats(modified,f)}]
	set nC [expr {$stats(pending,c) + $stats(modified,c)}]
	$w(c_status).l configure \
	    -text "$pwd: $stats(new,c)/$stats(new,f) new, $nC/$nF commented"

	# Get the filename at this line, bailing if we're past EOF.
	set tmp_fname [getName $curLine]
	if {"$tmp_fname" == ""} {
		set curLine $lastLine
		set lastLine 0
		return
	}
	# Prevents selecting ChangeSet unless some files are commented
	#puts "($tmp_fname n=($stats(new,c)) p=($stats(pending,c)) m=($stats(modified,c))"
	if {($tmp_fname == $csetFile) && (($stats(new,c) < 1) &&
	    ($stats(pending,c) < 1) && ($stats(modified,c) < 1))} {
		bottom "yell" \
"No files have comments yet, so no ChangeSet can be created.
Type Control-l to go back and provide some comments.\n"
		set curLine $lastLine
		set lastLine 0
		set tfname  [getName $curLine]
		# Make sure the user can't start typing in a "new file"
		if {([info exists status($tfname)] && 
		    $status($tfname) == "new") || ($tfname == $csetFile)} {
			$w(c_comments) configure -state disabled
			$w(c_comments) yview moveto 0
		}
		return
	}
	# Now do the colored highlighting for this line
	$w(c_files) tag remove "select" 1.0 end
	$w(c_files) \
	    tag add "select" "$curLine + 3 char" "$curLine lineend + 1 char"
	set filename $tmp_fname

	# remove warning message about not under rc if we checked this file
	if {[isNewfile $filename] && $commit($filename)} {
		bottom "" ""
		if {$status($filename) == "new"} {
			showFile $filename $gc(ci.display_bytes)
		}
	}
	$w(c_comments) configure -state normal
	$w(c_comments) delete 1.0 end
	set tmp [readComments $filename]
	if {"$tmp" != ""} {
		$w(c_comments) insert end $tmp
	}
	focus $w(c_comments)
	# If this is a done or new file, disable modification
	if {[regexp $file_rev $filename dummy file rev] || 
	    [isNewfile $filename]} {
		$w(c_comments) configure -state disabled
		$w(c_comments) yview moveto 0
	}
	$w(c_files) yview -pickplace $curLine
	set changed 0
	#puts "lastline=($lastLine) curline=($curLine)"
	# Need to check if we are on line 1.0 since deletes and 
	# discards on line 1.0 keeps the lastLine and curLine the same
	if {("$lastLine" != "$curLine") || (($lastLine == "1.0") &&
	    ($curLine == "1.0"))} { 
		cmd_diffs 
	}
	set state(clicked) ""
} ;# end doSelect

#
# Remove a file from the list of files that will be committed. If the
# file to exclude is pending, also need to exclude all newer revisions
#
proc excludeFile {cline fname} \
{
	global files file_rev commit icons status stats excluded w

	if {$status($fname) == "pending"} {
		set cat "pending"
    	} else { 
		set cat "non-pending"
	}
	# Save the icon that was there before we excluded it
	set icons($fname) $status($fname)
	changeBitmap $cline "exclude"
	set commit($fname) 0
	if {!$excluded($fname)} {
		incr stats($status($fname),f) -1
		set excluded($fname) 1
		#puts "setting fname=($fname) to exclude=1"
		set comments [readComments $fname]
		if {$comments != ""} {
			#puts "comments=($comments)"
			incr stats($status($fname),c) -1
		}
	}
	if {$cat == "non-pending"} { return }
	# Look to see whether there is a modified version of the pending file
	regexp $file_rev $fname dummy file rev
	set excluded_ver [lindex [split $rev .] 1]
	set match [$w(c_files) search $file 1.0]
	#puts "exclude match=($match) cline=($cline) fname=($fname)"
	# If found the sibling modified file, exclude it and update the
	# counters
	set seen($match) $match
	while {$match != ""} {
		#puts "match=($match)"
		set line [lindex [split $match .] 0]
		set name [getName "$line.0"]
		if {[regexp $file_rev $name dummy file rev]} {
			set ver [lindex [split $rev .] 1]
			#puts "rev=($rev) ver=($ver)"
		} else {
			# Exclude modified version if it exists
			set ver -1
		}
		if {($excluded_ver <= $ver) || ($ver == -1)} {
			set commit($name) 0
			set icons($name) $status($name)
			changeBitmap "$line.0" "exclude"
			if {!$excluded($name)} {
				incr stats($status($name),f) -1
				set excluded($name) 1
				#puts "setting n=($name) to exclude=1"
				if {[readComments $name] != ""} {
					incr stats($status($name),c) -1
				}
			}
		}
		set match [$w(c_files) search $file "$line.end"]
		# Don't loop around
		if {[info exists seen($match)]} {
			break
		} else {
			set seen($match) $match
		}
	}
	return
} ;# excludeFile

#
# Add a file back to the list of files that will be committed
#
proc includeFile {cline fname} \
{
	global files file_rev commit icons status stats excluded w

	if {$status($fname) == "modified"} {
		set cat "non-pending"
	} else {
		set cat "pending"
	}
	# bname: base name without version
	# fname: file name that is passed into this function from pixSelect
	if {[regexp $file_rev $fname dummy file rev]} {
		set included_ver [lindex [split $rev .] 1]
		set bname $file
	} else {
		set included_ver ""
		set bname $fname
	}
	#puts "fname=($fname) bname=($bname) included_ver=($included_ver)"
	# search for the base name, (i.e. without the @rev.num
	set match [$w(c_files) search $bname 1.0]
	set seen($match) $match
	while {$match != ""} {
		set line [lindex [split $match .] 0]
		set name [getName "$line.0"]
		if {[regexp $file_rev $name dummy file rev]} {
			set ver [lindex [split $rev .] 1]
		} else {
			# Include modified version if it exists
			# XXX: Oh my, this is yucky. This seems cobolish...
			set ver 999999
			set rev ""
		}
		#puts "i_v=($included_ver) rev=($rev) ver=($ver)"
		if {($included_ver == "") || ($ver <= $included_ver)} {
			if {$excluded($name)} {
				incr stats($status($name),f)
				##puts "setting fname=($name) to exclude=0"
				set excluded($name) 0
				if {[readComments $name] != ""} {
					#puts "add ($fname) to cmt lst (done)"
					incr stats($status($name),c)
					set commit($name) 1
					set icons($name) "done"
					changeBitmap "$line.0" $icons($name)
				} else {
					#puts "add ($name) to cmt lst (no-com)"
					set commit($name) 0
					set icons($name) "file"
					changeBitmap "$line.0" $icons($name)
				}
			}
		}
		set match [$w(c_files) search $bname "$line.end"]
		# Don't loop around
		if {[info exists seen($match)]} {
			break
		} else {
			set seen($match) $match
		}
	}
	if {$cat != "non-pending"} { return }
	set match [$w(c_files) search -regexp "$fname@\[1-9\]\.\[1-9\]*" 1.0]
	#puts "include match=($match) cline=($cline) fname=($fname)"
	while {$match != ""} {
		set line [lindex [split $match .] 0]
		set name [getName "$line.0"]
		if {$excluded($name) == 1} {
			##puts "adding file=($name) back to commit list"
			set icons($name) "done"
			changeBitmap "$line.0" $icons($name)
			set commit($name) 1
			incr stats($status($name),f)
			incr stats($status($name),c)
			set excluded($name) 0
			##puts "setting name=($name) to exclude=0"
		}
		set match [$w(c_files) search \
		    -regexp "$fname@\[1-9\]\.\[1-9\]*" $line.end]
		if {[info exists seen($match)]} {
			break
		} else {
			set seen($match) $match
		}
	}
	return
}

proc cmd_useSaved {} \
{
	global saved filename lastLine curLine w

	if {"$saved" == ""} { return }

	clearCheckin
	$w(c_comments) delete 1.0 end
	$w(c_comments) insert end $saved
	doNext 0
	doNext 1
}

# Clear the diffs screen and show the comments for all files.
# Used to give context to the user so they know what files are
# part of this cset
proc cmd_status {prefix} \
{
	global	stats csetFile filename status excluded w

	if {$prefix == ""} { doNext 0 }

	if {($stats(modified,c) == 0) && ($stats(pending,c) == 0) && \
	    ($stats(new,c) == 0)} {
		bottom "yell" \
"No files have comments yet!\nType Control-l to go back to diffs/history.\n"
		return
	}
	$w(c_lower).t configure -state normal
	$w(c_lower).t delete 1.0 end
	if {$prefix != ""} {
		foreach cc [split $prefix \n] {
			$w(c_lower).t insert end " $cc\n"
		}
		$w(c_lower).t tag add "notice" 1.0 "end - 1 char"
		$w(c_lower).t insert end "\n"
	}
	set line 1.0
	set filename [getName 1.0]
	while {"$filename" != ""} {
		set c [readComments $filename]
		if {("$c" != "") && !$excluded($filename)} {
			$w(c_lower).t insert end "$filename\n"
			regsub "\n+$" $c "" c
			foreach cc [split $c \n] {
				$w(c_lower).t insert end "    $cc\n"
			}
		}
		set line [$w(c_files) index "$line + 1 lines"]
		set filename [getName $line]
		#set filename [$w(c_files) get "$line + 4 char" "$line lineend"]
	}
	$w(c_lower).t configure -state disabled
}

proc nag {key} \
{
	global w

	catch {exec bk getmsg $key} err
	set widget .error
	toplevel $widget -background darkblue -bd 8 -relief flat
	wm transient $widget $w(c_top)
	set width [winfo width $w(c_top)]
	button $widget.ok \
	    -text OK \
	    -command "destroy $widget"
	catch {exec bk bin} bin
	set image [file join $bin "bklogo.gif"]
	if {[file exists $image]} {
		set bklogo [image create photo -file $image]
		label $widget.logo \
		    -image $bklogo \
		    -background white \
		    -bd 0 \
		    -relief ridge
		pack $widget.ok -side bottom -fill x 
		pack $widget.logo -side top -fill x 
	} else {
		pack $widget.ok -side bottom -fill x 
	}
	message $widget.m \
	    -text $err \
	    -aspect 400 \
	    -width $width
	pack $widget.m -side top -fill x 
	bind $widget <Visibility> "grab $widget; focus $widget"
	set x [expr {[winfo rootx $w(c_top)] + 50}]
	set y [expr {[winfo rooty $w(c_top)] + 30}]
	wm geometry $widget "+$x+$y"
	tkwait window $widget
}

proc confirm_logging {key addr} \
{
	global	confirm_answer commit w

	catch {exec bk getmsg $key $addr} err
	set widget .confirm_logging
	#puts "key=($key) addr=($addr)\nerr=($err)"
	toplevel $widget -background darkblue -bd 8 -relief flat
	wm transient $widget $w(c_top)
	set width [winfo width $w(c_top)]
	message $widget.m \
	    -text $err \
	    -width $width
	button $widget.ok \
	    -text "OK, send logs" \
	    -fg black -bg green \
	    -activebackground black \
	    -activeforeground green \
	    -command {
		global	confirm_answer
		set confirm_answer "yes"
		destroy .confirm_logging
	    }
	button $widget.no \
	    -text "Not OK, do not send" \
	    -fg white \
	    -bg darkblue \
	    -activebackground white \
	    -activeforeground darkblue \
	    -command {
		global	confirm_answer
		set confirm_answer "no"
		destroy .confirm_logging
	    }
	catch {exec bk bin} bin
	set image [file join $bin "bklogo.gif"]
	if {[file exists $image]} {
		set bklogo [image create photo -file $image]
		label $widget.logo -image $bklogo -bd 0 -background white
		grid $widget.logo -row 0 -column 0 -columnspan 2 -sticky ewns
		grid $widget.m -row 1 -column 0 -columnspan 2 -sticky ew
		grid $widget.ok -row 2 -column 0 -sticky ewns
		grid $widget.no -row 2 -column 1 -sticky ewns
		grid columnconfigure $widget 0 -weight 1
		grid columnconfigure $widget 1 -weight 1
	} else {
		grid $widget.m -row 0 -column 0 -columnspan 2 -sticky ew
		grid $widget.ok -row 1 -column 0 -sticky ew
		grid $widget.no -row 1 -column 1 -sticky ew
	}
	bind $widget <Visibility> "grab $widget; focus $widget"
	set x [expr {[winfo rootx $w(c_top)] + 40}]
	set y [expr {[winfo rooty $w(c_top)] + 20}]
	wm geometry $widget "+$x+$y"
	tkwait window $widget
	if { $confirm_answer == "yes" } { 
		set logging_ok [file join BitKeeper etc logging_ok]
		set commit($logging_ok) 1
		return 1 
	}
	return 0
}

proc ok_commit {} \
{
	catch { exec bk _logging } l
	if {[string match "*open logging OKed*" $l]} { return 1 }
	if {[string match "*open logging not OKed*" $l]} {
		catch { exec bk _loggingto } addr
		if {[confirm_logging "open_log_query" $addr] == 1} {
			catch { exec bk _loggingaccepted }
			return 1
		}
		return 0
	}
	if {[string match "*closed logging not OKed*" $l]} {
		catch { exec bk _loggingto } addr
		if {[confirm_logging "closed_log_query" $addr] == 1} {
			catch { exec bk _loggingaccepted }
			# Fall through to license checks
		} else {
			return 0
		}
	}
	if {[string match "*license is current*" $l]} { return 1 }
	if {[string match "*license expired, in grace*" $l]} {
		nag "license_grace"
		return 1
	}
	if {[string match "*license expired*" $l]} {
		nag "license_expired"
		return 0
	}
	nag "license_none"
	return 0
}

proc do_commit {comment} \
{
	global resolve commit tmp_dir file_rev files w

	if {[ok_commit] == 0} { return 1 }
	if {[llength $files(ignore)] > 0} {
		updateIgnoreFile
	}
	set tocommit [list]
	foreach f [array names commit] {
		set file ""
		if {$commit($f) != 1} { continue }
		# Need to strip of the @ so we can pass filename to sfind -C
		if {[regexp $file_rev $f dummy file rev]} {
			#puts stderr "rgx f=($f) val=($commit($f)) file=($file)"
		} else {
			#puts "f=$f val=$commit($f) file=$f"
			set file $f
		}
		set fd [open "| bk sfind -pC \"$file\"" "r"]
		gets $fd sfile
		catch {close $fd}
		if {$sfile == ""} { continue }
		# XXX: Shouldn't be calling sfind on a bunch of files
		# XXX: gross way of removing dups -- Not removing dups leads
		# to this error message from 'bk commit'. Tried sort|uniq
		# into bk pipe, but that didn't work
		# Redundant: akushner@pacbell.net|d9/e|20000819053043|31572|682e4f085819abc akushner@pacbell.net|d9/e|20000819053152|00835
		if {($sfile != "") && ![info exists dup($sfile)]} {
			set tocommit [concat $tocommit [list "$sfile"]]
			set dup($sfile) 1
		}
	}
	set cmt [file join $tmp_dir cmt[pid]]
	set cmtfd [open $cmt "w"]
	puts -nonewline $cmtfd $comment
	catch {close $cmtfd}
	set cfiles [join $tocommit "\n"]
	set cfiles_tmp [file join $tmp_dir cfiles[pid]]
	set cfilesfd [open $cfiles_tmp "w"]
	puts $cfilesfd $cfiles
	catch {close $cfilesfd} err
	set out [file join $tmp_dir commit[pid]]
	# Always use "commit -Yfile_name". Don't use "commit -y..." since 
	# NT can not escape certain quote/space sequence in comment
	cmd_busy 1
	set error ""
	if {$resolve != ""} {
		catch {
			exec bk commit $resolve -adqY$cmt -f$cfiles_tmp >&$out
		} error
	} else {
		catch {
			exec bk commit -adqY$cmt -f$cfiles_tmp >&$out
		} error
	}
	catch {file delete $cmt}
	popup "bk commit" $error $out
	catch {file delete $out $cfiles_tmp}
	return 0
}

proc popup {what error file} \
{
	global	w

	if {$error == "" && [file size $file] == 0} { return }
	set x [expr {[winfo rootx $w(c_top)] + [winfo width $w(c_top)] / 5} ]
	set y [expr [winfo rooty $w(c_top)] + 100]
	set in [open $file r]
	set out [open "| bk msg -geom +$x+$y -" w]
	if {$error == ""} {
		puts $out "Message from $what:\n"
	} else {
		puts $out "$what failed:\n"
	}
	while {[gets $in buf] >= 0} {
		puts $out $buf
	}
	catch {close $in}
	catch {close $out}
}

proc do_delta {comment filename} \
{
	global tmp_dir

	#puts "do_delta: bk delta -qy$comment $filename"
	set out [file join $tmp_dir delta[pid]]
	catch {exec bk delta -qy$comment $filename >&$out} error
	popup "bk delta" $error $out
	catch {file delete $out}
	if {$error != ""} { return "failed" }
	return "ok"
}

# Put file under revision control
proc do_new {filename} \
{
	catch {exec bk new $filename} err
	if {![string match "$filename revision 1.1:*" $err]} {
		tk_messageBox -type ok -icon info -message $err
		return "failed"
	}
	return "ok"
}

proc isNewfile {filename} \
{
	global files

	if {[lsearch -exact $files(new) "$filename"] >= 0 } {
		return 1
	}
	return 0
}

proc ci:commit {} \
{
	global	csetFile resolve file_rev tmp_dir commit stats files excluded
	global  w

	$w(c_lower).t configure -state normal
	$w(c_lower).t delete 1.0 end
	# XXX - should disable everything while doing this.
	cmd_busy 1
	set ret 0
	# When we commit files, we need to update the number of comments
	set line 1.0
	while {1} {
		set fname [$w(c_files) get "$line + 4 char" "$line lineend"]
		set line [$w(c_files) index "$line + 1 lines"]
		if {$fname == ""} { break }
		if {[regexp $file_rev $fname dummy file rev]} { continue }
		set c [readComments $fname]
		#puts "fname=($fname) c=($c)"
		if {("$c" == "") || $excluded($fname)} { continue }
		if {("$fname" != "$csetFile") && ($commit($fname) == 1)} {
			$w(c_lower).t insert end "delta $fname\n"
			if {[isNewfile $fname]} { 
				do_new $fname
			} elseif {[do_delta $c $fname] != "ok"} {
				#puts "do_delta of ($fname)"
				# incr stats($status($fname),c) -1
				set ret 1
				break
			}
		} else {
			$w(c_lower).t insert end "bk commit\n"
			set ret [do_commit $c]
		}
		deleteCommentfile $fname
	}
	$w(c_lower).t delete 1.0 end
	$w(c_lower).t configure -state disabled
	cmd_idle 0
	if {$ret != 0} {
		$w(c_comments) configure -state normal
		$w(c_comments) delete 1.0 end
		$w(c_comments) insert 1.0 $c
	}
	return $ret
}

# Remove std messages from comment window and replace with file's cset comments
# Used when starting to do a commit, and then selecting some other file
proc clearCheckin {} \
{
	global	filename msg w

	set c [$w(c_comments) get 1.0 "end - 1 char"]
	if {("$c" == "$msg(unedit)") || ("$c" == "$msg(gotCset)") ||
	    ("$c" == "$msg(noCset)") || ("$c" == "$msg(noCsetOK)") ||
	    ("$c" == "$msg(resolveCset)") || ("$c" == "$msg(onlyPending)") ||
	    ("$c" == "$msg(deleteNew)")} {
		$w(c_comments) configure -state normal
		$w(c_comments) delete 1.0 end
		set cc [readComments $filename]
		$w(c_comments) insert 1.0 $cc
	}
	if {("$c" == "$msg(noCset)") || ("$c" == "$msg(resolveCset)")} { 
		doLast 
    	}
}

proc isLocked {} \
{
	return [catch { exec bk lock -s } error]
}

# Remove the file from the file list and then updates the stats on
# the number of files and comments

proc removeFile {file {delete {}}} \
{
    	global filename curLine status stats tmp_dir csetFile w

	$w(c_comments) delete 1.0 end
	$w(c_files) configure -state normal
	$w(c_files) tag remove "select" 1.0 "end + 1 char"
	$w(c_files) delete $curLine "$curLine lineend + 1 char"
	$w(c_files) configure -state disabled
	set c [readComments $file]
	if {"$c" != ""} {
		incr stats($status($file),c) -1
		deleteCommentfile $file
	}
	incr stats($status($file),f) -1


	# Need to set filename here so when we delete the last file, 
	# we don't try to delete the ChangeSet file
	#set nextfile [getName [expr {$curLine + 1]}]
	#puts "nextfile=($nextfile) curLine=($curLine) last=($lastLine)"
	# Discard in the down direction, not up 
	#if {$curLine != "1.0"} {
	#	puts "going forward curLine=($curLine) last=($lastLine)"
	#	puts [getName [expr {$curLine + 1]}]
	#	doNext 1 
	#}
	set filename [getName $curLine]
	# Need to make sure we don't lose comments on the next file
	# This stuff probably shouldn't be here, but in doSelect or doNext?
	set comments [readComments $filename]
	if {"$comments" != ""} {
		$w(c_comments) configure -state normal
		$w(c_comments) insert end $comments
		$w(c_comments) configure -state disabled
	}
	#set lastLine $curLine
	#if {$curLine >= 1} { set curLine [expr $curLine - 1] }
	if {$filename == $csetFile} {
		doNext -1
	} else {
		doNext 0 
	}
	cmd_refresh 1
}

proc ci:cmd_checkin {} \
{
	global	gotCset stats resolve msg csetFile curLine lastLine argv
	global  w citool

	if {[ifBusy]} { return }
	if {[isLocked]} {
		tk_messageBox -type ok -icon info \
-message "Repository is locked,\ncan not check in at this time\nTry again later.";
		return
	}
	set c [$w(c_comments) get 1.0 "end - 1 char"]

	# After second click on Commit and we are in the correct state, 
	# do the Commit
	if {($c == $msg(gotCset)) || ($c == $msg(noCset)) || 
	    ($c == $msg(noCsetOK))} {
		ci:commit
		bottom "" ""
		set citool(exit) 200
		return
	} else {
		# XX: Save the current comments in case they
		# haven't been written out yet.
    	}
	# We don't allow checkins without comments or when there are only 
	# pending files
	set ncom [expr {$stats(pending,c) + $stats(modified,c) + $stats(new,c)}]
	if {("$c" == "") && ($ncom == 0)} { return }
	if {"$c" == "$msg(onlyPending)"} { return }

	# Need at least one comment other than the csetFile comment
	# to make a checkin
	if {$ncom == 0} {
		bottom "yell" \
"No files have comments yet, so no ChangeSet can be created.
Type Control-l to go back and provide some comments.\n"
		$w(c_comments) configure -state disabled
		$w(c_comments) yview moveto 0
		return
	}
	cmd_status ""
	$w(c_comments) configure -state normal
	$w(c_comments) delete 1.0 end
	set tmp [readComments $csetFile]
	if {($resolve != "") && ("$tmp" == "")} {
		$w(c_comments) insert end "$msg(resolveCset)"
	} elseif {($tmp == "") && ($stats(pending,c) == $stats(pending,f)) && \
	    ($stats(new,c) == 0) && ($stats(modified,c) == 0) } {
		$w(c_comments) insert end "$msg(onlyPending)"
	} elseif {$csetFile == ""} {
		$w(c_comments) insert end "$msg(noCsetOK)"
	} elseif {($tmp == "") && ($csetFile != "")} {
		$w(c_comments) insert end "$msg(noCset)"
	} else {
		$w(c_comments) insert end "$msg(gotCset)"
	}
	$w(c_comments) tag add "select" 1.0 "end + 1 char"
	$w(c_comments) configure -state disabled
	cmd_idle 1
}
# end: ci:cmd_checkin

#
# Check to make sure that the user really wants to unedit the file
# and that the file is one that can be deleted
#
proc cmd_unedit {} \
{
	global	stats curLine filename unedit tmp_dir msg status csetFile
	global  lastLine w

	if {[ifBusy]} { return }
	if {![info exists status($filename)]} { return }
	if {($filename == "$csetFile") || ($status($filename) == "pending")} { 
		return
	}
	set c [$w(c_comments) get 1.0 "end - 1 char"]
	if {("$c" != "$msg(unedit)") && ("$c" != "$msg(deleteNew)")} {
		doNext 0
		$w(c_comments) configure -state normal
		$w(c_comments) delete 1.0 end
		if {$status($filename) == "new"} {
			$w(c_comments) insert end "$msg(deleteNew)"
		} elseif {$status($filename) == "modified"} {
			$w(c_comments) insert end "$msg(unedit)"
		}
		$w(c_comments) tag add "select" 1.0 "end + 1 char"
		return
	}
	
	if {$status($filename) == "new"} {
		catch {file delete $filename} err
	} else {
		catch {exec bk unedit $filename} err
	}

	removeFile $filename
	doNext 0
}

# Read what's in the comment widget and save it.
# called by the 'cut comments' button
proc cmd_saved {} \
{
	global saved state w

	clearCheckin

	set saved [$w(c_comments) get 1.0 "end - 1 char"]
	if {"$saved" == ""} {
		set saved $state(oldsaved)
		return 0
	}
	$w(c_comments) delete 1.0 "end - 1 char"
	if {"$saved" != ""} {
		$w(c_menu).useSaved configure -state normal
	} else {
		$w(c_menu).useSaved configure -state disabled
	}
	set state(oldsaved) $saved
	after idle { doNext 0 }
	return 1
}

#
# After typing Control-l, this procedure restores the comments for 
# the file that is currently selected
#
proc cmd_restoreComments  {} \
{
	global curLine status w

	#set realLine [$w(c_files) index select.first]
	$w(c_comments) configure -state normal
	$w(c_comments) delete 1.0 end
	set filename [getName $curLine]
	set tmp [readComments $filename]
	if {"$tmp" != ""} {
		$w(c_comments) insert end $tmp
	}
	if {$status($filename) == "new"} {
		$w(c_comments) configure -state disabled
	} else {
		$w(c_comments) configure -state normal
	}
	focus $w(c_comments)
	cmd_refresh 0
}

proc cmd_refresh {restore} \
{
	global w

	if {$restore == 1} {
		set yview [lindex [$w(c_lower).t yview] 0]
	}
	cmd_diffs
	if {$restore == 1} {
		$w(c_lower).t yview moveto $yview
	}
}

#
# Toggles new files on and off the ignore list
#
proc cmd_ignore {} \
{
	global	filename files status curLine w

	if {$filename == ""} { return }
	set location [lsearch $files(ignore) $filename]
	if {$location == -1} {
		#puts "adding \"$filename\" to the ignore list"
		set files(ignore) [concat $files(ignore) [list "$filename"]]
		$w(c_files) tag add \
		    "dim" "$curLine + 3 char" "$curLine lineend + 1 char"
		set status($filename) "ignore"
	} else {
		#puts "removing \"$filename\" from the ignore list"
		set files(ignore) [lreplace $files(ignore) $location $location]
		$w(c_files) tag remove \
		    "dim" "$curLine + 3 char" "$curLine lineend + 1 char"
		set status($filename) "new"
	}
	bottom yell "File $filename placed on the ignore list"
	doNext 1
	return
}

#
# If files have been added to the ignore list, update the ignore file
# and check it in. Make sure that if it has been modified, that it will be
# passed to 'bk commit'
#
proc updateIgnoreFile {} \
{

	global files commit

	set ignored_files [join $files(ignore) "\n"]
	set ignorefile [file join BitKeeper etc ignore]
	set sdotignore [file join BitKeeper etc SCCS s.ignore]
	if {![file exists $sdotignore]} {
		set fd [open $ignorefile "w"]
		puts $fd $ignored_files
		catch {close $fd}
		catch {exec bk new $ignorefile} msg
	} elseif {[file writable $ignorefile]} {
		set fd [open $ignorefile "a"]
		puts $fd $ignored_files
		catch {close $fd}
		catch {exec bk edit $ignorefile} msg
		do_delta "Added $files(ignore) to the ignore list" $ignorefile
	} elseif {[file readable $ignorefile]} {
		catch {exec bk edit $ignorefile} msg
		set fd [open $ignorefile "a"]
		puts $fd $ignored_files
		catch {close $fd}
		do_delta "Added $files(ignore) to the ignore list" $ignorefile
	} else {
		# Something doesn't seem right -- probably not in a repo
		return
	}
	set commit($ignorefile) 1
}

proc cmd_history {} \
{
	global	file_rev status curLine

	set filename [getName $curLine]
	if {$filename == ""} { return }
	if {$status($filename) == "new"} { return }
	if {[regexp $file_rev $filename dummy file rev]} {
		catch {exec bk revtool $file &} err
	} else {
		catch {exec bk revtool $filename &} err
	}
}

proc cmd_busy {update} \
{
	global w

	$w(c_upper) configure -cursor watch
	$w(c_menu) configure -cursor watch
	$w(c_files) configure -cursor watch
	$w(c_comments) configure -cursor watch
	$w(c_lower).t configure -cursor watch
	if {$update != 0} { update }
}

proc cmd_idle {readonly} \
{
	global w

	$w(c_upper) configure -cursor left_ptr
	$w(c_menu) configure -cursor arrow
	$w(c_files) configure -cursor left_ptr
	if {$readonly == 1} {
		$w(c_comments) configure -cursor left_ptr
	} else {
		$w(c_comments) configure -cursor xterm
	}
	$w(c_lower).t configure -cursor left_ptr
	update
}

proc cmd_difftool {} \
{
	global	filename csetFile file_rev status difftool 

	if {![info exists status($filename)]} { return }
	if {[regexp $file_rev $filename]} { return }
	switch $filename {
	    ""		-
	    $csetFile	{return}
	}
	switch $status($filename) {
	    "new"	-
	    $csetFile	{return}
	}
	cmd_busy 1
	catch {exec bk difftool $filename &} err
	cmd_idle 0
}

proc cmd_diffs {} \
{
	global	filename diffsOpts csetFile file_rev commit msg
	global  gc files diffCount w

	cmd_busy 1
	clearCheckin
	if {$filename == ""} { 
		cmd_idle 0
		return
	}
	if {[isNewfile $filename] && ($commit($filename) == 0)} {
		bottom "yell" "$msg(nonrc)"
		showFile $filename $gc(ci.display_bytes)
		cmd_idle 0
		return
	}
	if {[isNewfile $filename] && ($commit($filename) == 1)} {
		cmd_idle 0
		return
	}
	$w(c_lower).t configure -state normal
	$w(c_lower).t delete 1.0 end
	if {$filename == $csetFile} {
		cmd_status \
"Please describe the change which is implemented in the deltas listed below.
Describe the change as an idea or concept; your description will be used by
other people to decide to use or not to use this changeset.

If you provide a description, the deltas will be grouped into a ChangeSet,
making them available to others.  If you do not want to do that yet, just
click Commit without typing in comments here and no ChangeSet will be made."

		$w(c_lower).t configure -state disabled
		cmd_idle 0
		return
	}
	if {[regexp $file_rev $filename dummy file rev]} {
		$w(c_lower).t insert end \
" This delta has been previously checked in and is in pending state.
 That means that you can not modify these comments, and that this delta
 will be included in the ChangeSet when you next create a ChangeSet.

"
		set d [open "| bk prs -hr$rev -nd:PARENT: \"$file\""]
		gets $d parent
		catch {close $d} err
		set d [open "| bk diffs $diffsOpts -R$rev \"$file\""]
		$w(c_lower).t insert end \
		    " bk diffs $diffsOpts -R$parent $file\n"
		$w(c_lower).t tag add "notice" 1.0 "end - 1 char"
		set l 6
		set readonly 1
	} else {
		set d [open "| bk sinfo \"$filename\""]
		while { [gets $d str] >= 0 } {
			$w(c_lower).t insert end "$str\n"
		}
		catch {close $d} err
		set d [open "| bk diffs $diffsOpts \"$filename\""]
		set l 2
		set readonly 0
	}
	$w(c_lower).t insert end "\n"
	#puts "=======================> in diffs filename=($filename)"
	gets $d str
	if {"$diffsOpts" == "-u"} {
		gets $d str
		gets $d str
	}
	set diffCount 0
	while { [gets $d str] >= 0 } {
		$w(c_lower).t insert end "$str\n"
		set l [incr l]
		if {"$diffsOpts" == "-u"} {
			if {[regexp {^\+} $str]} {
				incr diffCount
				$w(c_lower).t tag \
				    add "newTag" $l.0 "$l.0 lineend + 1 char"
			}
			if {[regexp {^\-} $str]} {
				incr diffCount
				$w(c_lower).t tag \
				    add "oldTag" $l.0 "$l.0 lineend + 1 char"
			}
		} else {
			if {[regexp {^\>} $str]} {
				incr diffCount
				$w(c_lower).t tag \
				    add "newTag" $l.0 "$l.0 lineend + 1 char"
			}
			if {[regexp {^\<} $str]} {
				incr diffCount
				$w(c_lower).t tag \
				    add "oldTag" $l.0 "$l.0 lineend + 1 char"
			}
		}
	}
	catch {close $d}
	$w(c_lower).t configure -state disabled
	$w(c_menu).history configure -text "History" -command cmd_history
	cmd_idle $readonly
}

# Currently not working -- The mark stuff above seems broken and there is
# no concept of range. A diff really spans until the next tag color or
# blank line. Need to fix
proc _dot {} \
{
	global  Diffs DiffsEnd diffCount lastDiff

	puts "trying to view lastdiff=($lastDiff) diffCount=($diffCount)"
	$w(c_lower).t see diff-$lastDiff
	#if {![info exists Diffs($lastDiff)]} {return}
	#scrollDiffs $Diffs($lastDiff) $DiffsEnd($lastDiff)
	#highlightDiffs $Diffs($lastDiff) $DiffsEnd($lastDiff)
}

proc scrollDiffs {start stop} \
{
	global  gc app w

	# Either put the diff beginning at the top of the window (if it is
	# too big to fit or fits exactly) or
	# center the diff in the window (if it is smaller than the window).
	set Diff [lindex [split $start .] 0]
	set End [lindex [split $stop .] 0]
	set size [expr {$End - $Diff}]
	# Center it.
	if {$size < $gc($app.diffHeight)} {
		set j [expr {$gc($app.diffHeight) - $size}]
		set j [expr {$j / 2}]
		set i [expr {$Diff - $j}]
		if {$i < 0} {
			set want 1
		} else {
			set want $i
		}
	} else {
		set want $Diff
	}

	set top [topLine]
	set move [expr {$want - $top}]
	$w(c_lower).t yview scroll $move units
	$w(c_lower).t xview moveto 0
	$w(c_lower).t see $start
}

proc widgets {{widget {}}} \
{
	global	diffsOpts pixelsPerLine firstConfig halfScreen
	global	edit_busy comments bmap tcl_platform csetFile msg
	global  gc state d search app diffCount app w
	global  commit dup status excluded icons

	array_unset commit; array set commit [list]
	array_unset dup; array set dup [list]
	array_unset status; array set status [list]
	array_unset excluded; array set excluded [list]
	array_unset icons; array set icons [list]

	option add *background $gc(BG)
	# Defaults
	if {$tcl_platform(platform) == "windows"} {
		set y 0
		set gc(py) -2; set gc(px) 1; set gc(bw) 2
	} else {
		set y 1
		set gc(py) 1; set gc(px) 4; set gc(bw) 2
	}
	# background color for _some_ of the filetype icons. 
	set color #e8f8a6
	set diffsOpts "-u"
	set firstConfig 1
	set edit_busy 0
	set csetFile "ChangeSet"
# Don't make comments wider than 65 chars
#--------|---------|---------|---------|---------|---------|----
	set msg(nonrc) "
  Not currently under revision control. 
  Click on the file-type icon if you want to include this 
  file in the current ChangeSet\n"
	set msg(gotCset) "
  Click \[Commit] again to check in and create this ChangeSet,
  or type Control-l to go back to back and work on the comments.\n"
	set msg(onlyPending) "
  Since there are only pending files selected, you must
  create a ChangeSet comment in order to commit.\n
  Type Control-l to go back and provide ChangeSet comments.\n"
	set msg(noCset) "
  Notice: this will not group and commit the deltas listed below
  into a ChangeSet, because there are no ChangeSet comments.
  Click \[Commit] again to check in only the commented deltas,
  or type Control-l to go back and provide ChangeSet comments.\n"
	set msg(resolveCset) "
  You must provide comments for the ChangeSet file when resolving.
  Type Control-l to go back and do so.\n"
	set msg(noCsetOK) "
  Click \[Commit] again to check in and create these deltas,
  or type Control-l to go back to back and work on the comments.\n"
	set msg(unedit) "
  Click \[Discard] again if you really want to unedit this file,
  or type Control-l to go back and work on the comments.\n
  Warning!  The changes to this file shown below will be lost.\n"
	set msg(deleteNew) "
  Click \[Discard] again if you really want to delete this file,
  or type Control-l to leave this file in place.\n
  Warning!  The file below will be deleted if you click \[Discard]\n"
	set bmap(new) [image create bitmap -background $color -data {
#define q_width 11
#define q_height 13
static unsigned char q_bits[] = {
   0xff, 0x07, 0x01, 0x04, 0x79, 0x04, 0xcd, 0x04, 0x85, 0x04, 0x81, 0x04,
   0x61, 0x04, 0x31, 0x04, 0x31, 0x04, 0x01, 0x04, 0x31, 0x04, 0x31, 0x04,
   0xff, 0x07};
	}]
	set bmap(modified) [image create bitmap -background $color -data {
#define f_width 11
#define f_height 13
static unsigned char f_bits[] = {
   0xfc, 0x07, 0x04, 0x04, 0xf7, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04,
   0xfd, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04,
   0xff, 0x07};
   	}]
	set bmap(cset) [image create bitmap -background #b0b0e0 -data {
#define f_width 11
#define f_height 13
static unsigned char f_bits[] = {
   0xfc, 0x07, 0x04, 0x04, 0xf7, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04,
   0xfd, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04, 0xfd, 0x05, 0x01, 0x04,
   0xff, 0x07};
   	}]
	set bmap(done) [image create bitmap -background $color -data {
#define file_width 11
#define file_height 13
static unsigned char file_bits[] = {
   0xff, 0x07, 0x01, 0x04, 0x01, 0x04, 0x01, 0x05, 0x85, 0x05, 0xcd, 0x05,
   0xed, 0x04, 0x7d, 0x04, 0x3d, 0x04, 0x1d, 0x04, 0x09, 0x04, 0x01, 0x04,
   0xff, 0x07};
   	}]
	set bmap(exclude) [image create bitmap \
	    -background $gc(ci.textBG) \
	    -foreground $gc(ci.excludeColor)\
	    -data {
#define exc2_width 11
#define exc2_height 13
static unsigned char exc2_bits[] = {
   0x01, 0x04, 0x03, 0x06, 0x07, 0x07, 0x8e, 0x03, 0xdc, 0x01, 0xf8, 0x00,
   0x70, 0x00, 0x70, 0x00, 0xf8, 0x00, 0xdc, 0x01, 0x8e, 0x03, 0x07, 0x07,
   0x03, 0x06};
   	}]

	if {$widget == ""} {
		set w(c_top) .__citool
		catch {destroy $w(c_top)} err
		toplevel $w(c_top)
	} else {
		set w(c_top) $widget
	}

	set w(c_status) $w(c_top).status
	set w(c_upper) $w(c_top).upper
	set w(c_lower) $w(c_top).lower
	set w(c_files) $w(c_top).upper.files
	set w(c_comments) $w(c_top).upper.comments
	set w(c_menu) $w(c_top).menu
	set w(c_search) $w(c_top).search
	wm title $w(c_top) "Check In Tool"
	if {"$gc(ci.geometry)" != ""} {
		wm geometry $w(c_top) $gc(ci.geometry)
	}

	# Display specific junk
	set rootX [winfo screenwidth $w(c_top)]
	set rootY [winfo screenheight $w(c_top)]
	set diffCount 0
	set halfScreen [expr {$gc(ci.diffHeight) / 2}]
	set state(oldsaved) ""
	set state(clicked) ""
	set state(oldclicked) ""
	set state(toggle_all) "false"

# XXX: These bitmaps should be in a library!
image create photo prevImage \
    -format gif -data {
R0lGODdhDQAQAPEAAL+/v5rc82OkzwBUeSwAAAAADQAQAAACLYQPgWuhfIJ4UE6YhHb8WQ1u
WUg65BkMZwmoq9i+l+EKw30LiEtBau8DQnSIAgA7
}
image create photo nextImage \
    -format gif -data {
R0lGODdhDQAQAPEAAL+/v5rc82OkzwBUeSwAAAAADQAQAAACLYQdpxu5LNxDIqqGQ7V0e659
XhKKW2N6Q2kOAPu5gDDU9SY/Ya7T0xHgTQSTAgA7
}
	frame $w(c_status) -borderwid 1
	    label $w(c_status).l \
		-font $gc(ci.fixedFont) \
		-wid 84 \
		-relief groove
	    grid $w(c_status).l -sticky ew

	frame $w(c_upper) -borderwidth 1 -relief raised
	    text $w(c_files) \
		-spacing1 1 \
		-spacing3 1 \
		-wrap none \
		-bg $gc(ci.listBG) \
		-fg $gc(ci.textFG) \
		-font $gc(ci.fixedFont) \
		-width 70 \
		-height $gc(ci.filesHeight) \
		-xscrollcommand [list $w(c_upper).x1scroll set ] \
		-yscrollcommand [list $w(c_upper).y1scroll set ]
	    scrollbar $w(c_upper).x1scroll \
		-orient horiz \
		-width $gc(ci.scrollWidth) \
		-command "$w(c_files) xview" \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    scrollbar $w(c_upper).y1scroll \
		-width $gc(ci.scrollWidth) \
		-command "$w(c_files) yview" \
		-troughcolor $gc(ci.troughColor) \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    text $w(c_comments) \
		-wrap none \
		-font $gc(ci.fixedFont) \
		-width 70 \
		-height $gc(ci.commentsHeight) \
		-bg $gc(ci.textBG) \
		-fg $gc(ci.textFG) \
		-xscrollcommand [list $w(c_upper).x2scroll set] \
		-yscrollcommand [list $w(c_upper).y2scroll set]
	    scrollbar $w(c_upper).x2scroll -orient horiz \
		-width $gc(ci.scrollWidth) \
		-command "$w(c_comments) xview" \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    scrollbar $w(c_upper).y2scroll -width $gc(ci.scrollWidth) \
		-command "$w(c_comments) yview" \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    grid $w(c_files) -row 0 -column 0 -sticky ew
	    grid $w(c_upper).y1scroll -row 0 -column 1 -sticky nse -rowspan 2
	    grid $w(c_upper).x1scroll -row 1 -column 0 -sticky ew
	    grid $w(c_comments) -row 2 -column 0 -sticky ew
	    grid $w(c_upper).y2scroll -row 2 -column 1 -sticky nse -rowspan 2
	    grid $w(c_upper).x2scroll -row 3 -column 0 -sticky ew

	frame $w(c_menu) -borderwid 1 -relief raised
		button $w(c_menu).useSaved \
		    -state disabled \
		    -bg $gc(ci.buttonColor) \
		    -font $gc(ci.buttonFont) -text "Paste\ncomments" \
		    -pady $y -command cmd_useSaved
		button $w(c_menu).gcomments \
		    -bg $gc(ci.buttonColor) \
		    -font $gc(ci.buttonFont) \
		    -text "Cut\ncomments" \
		    -pady $y -command cmd_saved
		button $w(c_menu).history \
		    -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "History" \
		    -command cmd_history
		button $w(c_menu).checkin -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "Checkin" \
		    -command ci:cmd_checkin
		set m $w(c_menu).edit.m
		menubutton $w(c_menu).edit -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -relief raised \
		    -text "Edit" \
		    -menu $m
		menu $m
		    $m add command -label "Fmtool" -command "cmd_edit fmtool"
		    $m add command -label "TK editor" -command "cmd_edit gui"
		    $m add command -label "Xterm editor" \
			-command "cmd_edit xterm"
		button $w(c_menu).difftool -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "Diff tool" \
		    -command cmd_difftool
		button $w(c_menu).unedit -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "Discard" \
		    -command cmd_unedit
		button $w(c_menu).help -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "Help" \
		    -command { exec bk helptool citool & }
		button $w(c_menu).quit -bg $gc(ci.buttonColor) \
		    -pady $y \
		    -font $gc(ci.buttonFont) \
		    -text "Quit" \
		    -command {
			# XXX - what I really want is a way to say "no more
			# clicks are allowed on this button".  I tried
			# configuring the state to disabled but that didn't
			# work.
			cmd_done
		    }

		grid $w(c_menu).gcomments -sticky ew
		grid $w(c_menu).useSaved -sticky ew
		grid $w(c_menu).checkin -sticky ew
		grid $w(c_menu).edit -sticky ew
		grid $w(c_menu).history -sticky ew
		grid $w(c_menu).difftool -sticky ew
		grid $w(c_menu).unedit -sticky ew
		grid $w(c_menu).help -sticky ew
		grid $w(c_menu).quit -sticky ew

	frame $w(c_lower) -borderwidth 1 -relief raised
	    text $w(c_lower).t -width 81 -height $gc(ci.diffHeight) \
		-font $gc(ci.fixedFont) \
		-wrap none \
		-bg $gc(ci.textBG) \
		-fg $gc(ci.textFG) \
		-xscrollcommand [list $w(c_lower).xscroll set] \
		-yscrollcommand [list $w(c_lower).yscroll set]
	    scrollbar $w(c_lower).xscroll \
		-orient horizontal \
		-width $gc(ci.scrollWidth) \
		-command [list $w(c_lower).t xview] \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    scrollbar $w(c_lower).yscroll \
		-orient vertical \
		-width $gc(ci.scrollWidth) \
		-command [list $w(c_lower).t yview] \
		-troughcolor $gc(ci.troughColor) \
		-background $gc(ci.scrollColor)
	    grid $w(c_lower).t -row 0 -column 0 -sticky nsew
	    grid $w(c_lower).yscroll -row 0 -column 1 -sticky ns
	    grid $w(c_lower).xscroll -row 1 -column 0 -columnspan 2 -sticky ew

	frame $w(c_search)
	search_widgets $w(c_search) $w(c_lower).t

	grid $w(c_status) -row 0 -column 0 -columnspan 2 -sticky ew
	grid $w(c_upper) -row 1 -column 0 -stick ew
	grid $w(c_menu) -row 1 -column 1
	grid $w(c_lower) -row 2 -column 0 -columnspan 2 -sticky nsew
	grid $w(c_search) -row 3 -column 0 -columnspan 2 -sticky w 

	grid rowconfigure $w(c_lower) 0 -weight 1
	grid rowconfigure $w(c_top) 2 -weight 1
	grid rowconfigure $w(c_top) 3 -weight 0
	#grid rowconfigure . 2 -weight 1
	#grid rowconfigure . 3 -weight 0

	grid columnconfigure $w(c_status) 0 -weight 1
	grid columnconfigure $w(c_upper) 0 -weight 1
	grid columnconfigure $w(c_lower) 0 -weight 1
	grid columnconfigure $w(c_top) 0 -weight 1
	#grid columnconfigure . 0 -weight 1

	# Bindings.
	bind $w(c_lower).t <Configure> {
		global gc pixelsPerLine firstConfig

		set x [winfo height $w(c_lower).t]
		# This gets executed once, when we know how big the text is
		if {$firstConfig == 1} {
			set h [winfo height $w(c_lower).t]
			set pixelsPerLine [expr {$h / $gc(ci.diffHeight)}]
			set firstConfig 0
		}
		set x [expr {$x / $pixelsPerLine}]
		set gc(ci.diffHeight) $x
		set halfScreen [expr {$gc(ci.diffHeight) / 2}]
	}
	$search(widget) tag configure search \
	    -background $gc(ci.searchColor) \
	    -font $gc(ci.fixedBoldFont)
	search_keyboard_bindings "no_slash"
	searchreset

	set t $w(c_comments)
	bind $t <Alt-u>		"cmd_lerase"
	bind $t <Alt-w>		"cmd_werase"
	bind $t <BackSpace>	{ checkEmpty "bs" }
	bind $t <Control-x>	cmd_saved
	bind $t <Control-c>	{ if {[cmd_saved]} {
					cmd_useSaved
					doNext -1
				  }
				}
	bind $t <Control-v>	cmd_useSaved
	bind $t <Control-b>	"$w(c_lower).t yview scroll -1 pages; break"
	bind $t <Control-d>	{ scroll 1 1; break }
	bind $t <Control-e>	"$w(c_lower).t yview scroll 1 units; break"
	bind $t <Control-f>	" $w(c_lower).t yview scroll 1 pages; break"
	bind $t <Control-l>	"cmd_restoreComments"
	bind $t <Control-n>	"doNext 1"
	bind $t <Control-p>	"doNext -1"
	bind $t <Control-u>	{ scroll -1 1; break }
	bind $t <Control-w>	"cmd_werase"
	bind $t <Control-y>	"$w(c_lower).t yview scroll -1 units; break"
	bind $t <Control-Return> "ci:cmd_checkin"
	bind $t <Delete>	{ checkEmpty "del" }
	bind $t <End>		"$w(c_lower).t yview -pickplace end; break"
	bind $t <$gc(ci.quit)>	"cmd_done"
	bind $t <Home>		"$w(c_lower).t yview -pickplace 1.0; break"
	bind $t <Key>		{ if {"%A" != "{}"} { top_changed "%A" } }
	bind $t <Next>		{ scroll 1 0; break }
	bind $t <Prior>		{ scroll -1 0; break }
	bind $t <Control-t>	{ toggle_state ; break}
	bind $t <Shift-Control-t> { select_all_new ; break}
	bind $t <Shift-Down>	"$w(c_lower).t yview scroll 1 units; break"
	bind $t <Shift-Up>	"$w(c_lower).t yview scroll -1 units; break"
	bind $w(c_files) <Button-1> { doPixSelect %x %y }
	if {$tcl_platform(platform) == "windows"} {
		bind $w(c_top) <MouseWheel> {
		    if {%D < 0} {
		    	$w(c_lower).t yview scroll 1 pages
		    } else {
		    	$w(c_lower).t yview scroll -1 pages
		    }
		}
		bind $w(c_top) <Shift-MouseWheel> {
		    if {%D < 0} {
			$w(c_files) yview scroll 1 pages
		    } else {
			$w(c_files) yview scroll -1 pages
		    }
		}
	} else {
		bind $w(c_top) <Button-4> \
		    "$w(c_lower).t yview scroll -1 pages"
		bind $w(c_top) <Button-5> \
		    "$w(c_lower).t yview scroll 1 pages"
		bind $w(c_top) <Shift-Button-4> \
		    "$w(c_files) yview scroll -1 pages"
		bind $w(c_top) <Shift-Button-5> \
		    "$w(c_files) yview scroll 1 pages"
		bind Text <ButtonRelease-2> {
			catch {
				%W insert insert [selection get]
				%W yview -pickplace insert
				after idle { doNext 0 }
			}
		}
	}
	# More display specific stuff & tag stuff
	$w(c_lower).t tag configure "newTag" -background $gc(ci.newColor)
	$w(c_lower).t tag configure "oldTag" -background $gc(ci.oldColor)
	# XXX - different names for yell/select?
	$w(c_lower).t tag configure "yell" -background $gc(ci.warnColor)
	$w(c_lower).t tag configure "notice" -background $gc(ci.noticeColor)
	$w(c_files) tag configure "select" -background $gc(ci.selectColor) \
	    -relief ridge -borderwid 1
	$w(c_files) tag configure "dim" -foreground grey50
	$w(c_files) tag configure "normal" -foreground black
	$w(c_comments) tag configure "select" -background $gc(ci.selectColor)\
	    -relief groove -borderwid 1
	#$w(c_files) configure -selectbackground $gc(ci.textBG) \
	#    -selectborderwidth 0
	# prevent selection in file list
	bindtags $w(c_files) [list $w(c_files) $w(c_top) all]
	$w(c_top) configure -bg $gc(BG)
	# In the search window, don't listen to "all" tags.
	bindtags $search(text) [list $w(c_search).search Entry $w(c_top) ]
	if {$widget == ""} {
		wm deiconify $w(c_top)
		wm protocol $w(c_top) WM_DELETE_WINDOW { cmd_done }
	}
}

proc scroll {dir half} \
{
	global	gc halfScreen w

	if {$half == 1} {
		set nlines $halfScreen
	} else {
		set nlines $gc(ci.diffHeight)
		incr nlines -1
	}
	set nlines [expr {$dir * $nlines}]
	$w(c_lower).t yview scroll $nlines  units
}

#
# Read in pending type file and update the file list
#
proc doPending {fname} \
{
	global	stats file_rev commit status icons bmap

	if {$fname == ""} {return}
	set status($fname) "pending"
	incr stats($status($fname),f)
	incr stats($status($fname),c)
	set commit($fname) 1
	set icons($fname) "done"

	set lfile_rev {(.*)@([0-9].*)}
	regexp $lfile_rev $fname dummy file rev
	# get comments
	# XXX: PENDING none of this is needed
	set d [open "| bk prs -h {-d\$each(:C:){(:C:)\n}} -r$rev \"$file\""]
	#puts "In doPending fname=($fname) rev=($rev) file=($file)"
	set ctmp ""
	while { [gets $d c] >= 0 } {
		set ctmp "$ctmp$c\n"
	}
	catch {close $d} err
	# Don't have empty comments for checked in files and life is better.
	if {$ctmp == ""} {
		set ctmp "(NO COMMENTS AVAILABLE)"
	}
	if {$ctmp != "\n\n"} {
		regsub "\n+$" $ctmp "" ctmp
	} elseif {$rev == "1.1"} {
		set ctmp "New BitKeeper file ``$file''"
	}
	saveComments $fname $ctmp
	# XXX: Keep
	insertFile "pending" "done" $fname
}

# Place the file in the files window. Try to keep categories together with
# the use of marks
proc insertFile {mark bitmap fname} \
{
	global bmap m w

	if {$bitmap == "pending"} {set bitmap "done"}
	switch $mark {
	    modified	{set i $m(m)}
	    pending	{set i $m(p)}
	    new		{set i $m(n)}
	}
	$w(c_files) mark set insert "$i"
	$w(c_files) insert insert " "
	$w(c_files) image create insert -image $bmap($bitmap)
	$w(c_files) insert insert "  $fname\n"
	# modified at top, followed by new, then pending
	# The way sfind works, the modified and pending come first (and
	# sometimes both a pending and modified on the same line). The 
	# new files come last, so we need to keep a pointer to the last
	# modified (changed) file entry so we can start inserting the 
	# new files there when we get there.
	switch $mark {
	    modified	{
    				set m(m) [$w(c_files) index insert]
    				set m(p) [expr $m(p) + 1]
    				set m(n) $m(m)
				#puts "\tmodified m=($m(m)) p=($m(p)) n=($m(n))"
			}
	    pending	{
    				set m(p) [$w(c_files) index insert]
    				set m(n) $m(m)
			}
	    new		{
    				set m(n) [expr $m(n) + 1]
			}
	}
}

proc readSfiles {fd} \
{
	global	csetFile stats commit status bmap icons files excluded
	global  m

	if {[eof $fd]} {
		catch {close $fd}
		return
	}
	array set m {
	   m 1.0
	   n 1.0
	   p 1.0
	}
	while { [gets $fd str] >= 0 } {
		#puts "str=($str)"
		# Extract the filename and the revision number
		# For Pending files, keep the revision number attached
		set lstate [string range $str 0 0]
		set cstate [string range $str 1 1]
		set pstate [string range $str 2 2]
		set file [string range $str 5 [string length $str]]

		# handle bugs for files named '~foobar'
		if {[string index $file 0] == "~"} {
		    set file "./$file"
		}

		set index [string last "|" $file]
		# Strip off the revision number and recreate the file@rev
		# version (fname_rev). The | separator does not look good
		# in the file list. 
		set fname_rev ""
		set fname ""
		if {$index >= 0} {
			set fname [string range $file 0 [expr {$index - 1}]]
			set rnum [string range $file [expr {$index + 1}] end]
			set fname_rev "$fname@$rnum"
			#puts stderr "fname=($fname) r=($rnum) f_r=($fname_rev)"
			set excluded($fname_rev) 0
		} else {
			set fname $file
		}
		set excluded($fname) 0
		# Work on a file that is pending, but also has new work 
		if {($cstate == "c") && ($pstate == "p")} {
			# Only add modified files that we haven't alreay 
			# processed otherwise, cases like
			#    lcp  gui/difftool.tcl|1.30
			#    lcp  gui/difftool.tcl|1.29
			# will cause multiple entries for the modified file
			# in the file listing
			if {![info exists seen($fname)]} {
				set status($fname) "modified"
				incr stats($status($fname),f)
				set files(changed) [concat $files(changed) \
				    [list "$fname"]]
				#puts "\nadding fname=($fname)"
			}
			set comment [readComments $fname]
			if {$comment != ""} {
				incr stats($status($fname),c)
				set icons($fname) "done"
				set commit($fname) 1
			} else {
				set icons($fname) "modified"
				set commit($fname) 0
			}
			if {![info exists seen($fname)]} {
				insertFile modified $icons($fname) $fname
			}
			# Now do the pending file
			doPending $fname_rev
			set seen($fname) $fname
			#puts "setting seen($fname)=$fname $seen($fname)"
		} elseif {($cstate == " ") && ($pstate == "p")} { 
			doPending $fname_rev
		} elseif {($cstate == "c") && ($pstate != "p")} { 
			set status($fname) "modified"
			incr stats($status($fname),f)
			set files(changed) [concat $files(changed) \
			    [list "$fname"]]
			set comment [readComments $fname]
			if {$comment != ""} {
				incr stats($status($fname),c)
				set icons($fname) "done"
				set commit($fname) 1
			} else {
				set icons($fname) "modified"
				set commit($fname) 0
			}
			insertFile modified $icons($fname) $fname
		} elseif {$cstate == "x"} { 
			set status($fname) "new"
			incr stats($status($fname),f)
			set files(new) [concat $files(new) [list "$fname"]]
			#lappend files(new) "$fname"
			set comment [readComments $fname]
			if {$comment != ""} {
				incr stats($status($fname),c)
				set commit($fname) 1
				set icons($fname) "done"
			} else {
				set commit($fname) 0
				set icons($fname) "new"
			}
			insertFile new $icons($fname) $fname
		}
	}
	return
}

# Get the list of files and add them to the files text widget
# If we are at the root of the tree, then add the ChangeSet file
proc getFiles {input} \
{
	global	filename curLine lastLine saved dev_null pwd argv
	global	tmp_dir csetFile stats status bmap resolve file_rev 
	global  commit files icons excluded w

	cmd_busy 0
	$w(c_files) configure -state normal
	$w(c_files) delete 1.0 end
	$w(c_comments) delete 1.0 end
	# type,c -- number of comments for that type of file
	# type,f -- number of that type of file
	foreach t {pending new modified csetFile} {
		set stats($t,c) 0
		set stats($t,f) 0
    	}
	#trace variable stats wu "s_trace"
	if {[file isdirectory [file join BitKeeper etc]]} {
		catch {exec bk clean ChangeSet 2> $dev_null} err
	}
	set files(changed) [list]
	set files(new) [list]
	set files(ignore) [list]
	set fd [open $input "r"]
	readSfiles $fd
	set file_rev {(.*)@([0-9].*)}
	catch {close $fd} err
	catch {file delete $input} err
	# Reset the file/revision separator -- keeping the separator as the
	# '@' character while in citool
	if {[numFiles] == 0} {
		puts "There are no files that require checkin."
		exit 0
	}
	wm deiconify $w(c_top)
	# Add an extra line item for the ChangeSet file.
	if {[file isdirectory [file join BitKeeper etc]]} {
		set status($csetFile) "csetFile"
		incr stats($status($csetFile),f)
		set commit($csetFile) 0
		set tmp [readComments $csetFile]
		if {"$tmp" != ""} {
			$w(c_comments) insert end $tmp
			incr stats($status($csetFile),c)
			set img "done"
			$w(c_menu).checkin configure -text "Commit"
		} else {
			set img "cset"
		}
		$w(c_files) insert end " "
		$w(c_files) image create end -image $bmap($img)
		$w(c_files) insert end "  $csetFile\n"
		set icons($csetFile) $img
		set excluded($csetFile) 0
	} else {
		set csetFile ""
	}
	# Now clean up, highlight the 1st item, and get ready for doing stuff
	bottom "" ""
	$w(c_files) delete "end - 1 char" end
	$w(c_files) configure -state disabled
	$w(c_files) tag remove "select" 1.0 end
	$w(c_files) tag add "select" 1.3  "1.0 lineend"
	set filename ""
	set curLine 1.0
	set lastLine 0.0
	set saved ""
	set pwd [ pwd ]
	# If all files in pending state, only need to add changeset comment
	# before being able to commit
	if {($stats(new,f) == 0) && ($stats(modified,f) == 0) &&
	    ($stats(pending,f) > 0)} {
		cmd_idle 0
	} else {
		cmd_idle 1
	}
	doSelect
}

# XXX: should check to see whether ignore list has been modified
proc cmd_done {} \
{
	global	stats w citool

	if {[ifBusy]} { return }
	doNext 0
	# Any comments? check whether user wants them saved
	if {($stats(new,c) > 0) || ($stats(modified,c) > 0)} {
		ci:save_and_exit
		$w(c_menu).quit configure -state normal
	} else {
		set citool(exit) 1
	}
}

# XXX: should also save ignore list
proc ci:save_and_exit {} \
{
	global citool w

	set ret [catch {toplevel .c}]
	if {$ret != 0} { return }
	.c config -borderwidth 10
	    frame .c.save -borderwidth 2 -relief sunken
		button .c.save.b \
		    -text "Quit and save comments\n(recommended)" \
		    -command [list set citool(exit) 1]
		pack .c.save.b -side top -fill both -padx 5 -pady 5
	    frame .c.quit -borderwidth 2
		button .c.quit.b \
		    -text "Quit without saving comments" \
		    -command "deleteallComments; set citool(exit) 2"
		pack .c.quit.b -fill both -padx 5 -pady 5
	    frame .c.cancel  -borderwidth 2
		button .c.cancel.b \
		    -text "Cancel" \
		    -command "destroy .c"
		pack .c.cancel.b -fill both -padx 5 -pady 5
	pack .c.save -side top  -fill both
	pack .c.quit -side top  -fill both
	pack .c.cancel -side top -fill both
	set x [expr {[winfo rootx $w(c_top)] + [winfo width $w(c_top)] - 250}]
	set y [expr {[winfo rooty $w(c_top)] + 120}]
	wm geometry .c "+$x+$y"
	wm transient .c $w(c_top)
}

# XXX: PENDING If pending, shouldn't get here
proc saveComments {filename comments} \
{
	if {$comments == ""} {return} ;# Don't need to make zero length c.files
	set dir [file dirname $filename]
	set fname [file tail $filename]
	set dname [file join $dir SCCS]
	set name [file join $dir SCCS c.$fname]
	if {![file isdirectory "$dname"]} {
		file mkdir "$dname"
	}
	set fd [open "$name" "w"]
	puts $fd $comments
	catch {close $fd}
	#puts "saveComments: dir=($dir) fname=($fname) comments=($comments)"
	return
}

# XXX: PENDING If pending, get the cmts from the prs command
proc readComments {filename} \
{
	set dir [file dirname $filename]
	set fname [file tail $filename]
	set name [file join $dir SCCS c.$fname]
	if {[file exists "$name"]} {
		set fd [open "$name" "r"]
		set cmts [read -nonewline $fd]
		catch {close $fd}
	} else {
		set cmts ""
	}
	return $cmts
}

# XXX: PENDING If pending, shouldn't get here
proc commentfileExists {filename} \
{
	set dir [file dirname $filename]
	set fname [file tail $filename]
	set name [file join $dir SCCS c.$fname]
	if {[file exists "$name"]} {
		return 1
	}
	return 0
}

# XXX: PENDING If pending, shouldn't get here
proc deleteCommentfile {filename} \
{
	set dir [file dirname $filename]
	set fname [file tail $filename]
	set name [file join $dir SCCS c.$fname]
	if {[file exists "$name"]} {
		#puts "removing comments for file: $filename"
		file delete "$name"
	}
}

proc deleteallComments {} \
{
	global files csetFile

	set name [file join SCCS c.$csetFile]
	foreach str [lsort $files(changed)] {
		deleteCommentfile $str
	}
	foreach str [lsort $files(new)] {
		deleteCommentfile $str
	}
	if {[file exists "$name"]} {
		file delete "$name"
	}
}

proc citool_read {} \
{
	global	citool progress_exit

	while {[gets $citool(fd) buf] > 0} {
		if {$citool(nfiles) > 0} {
			set l [split $buf]
			set sfiles [lindex $l 0]
			set extras [lindex $l 1]
			set dirs [lindex $l 2]
			set changed [lindex $l 3]
			set n [expr {100 * $sfiles / $citool(nfiles)}]
			set a [format "%d directories processed" $dirs]
			set b [format "%d sfiles processed" $sfiles]
			set c [format "%d modified files found" $changed]
			set d [format "%d extra files found" $extras]
			set l [list $a $b $c $d]
			progress $n $l
		}
	}
	if {[eof $citool(fd)]} { set citool(done) 1; close $citool(fd); }
	if {$citool(nfiles) > 0} {
		if {$progress_exit} { exit }
	}
}

proc nfiles {} \
{
	set fd [open "|bk -R prs -hr+ {-d:HASHCOUNT:\n} ChangeSet" "r"]
	if {[gets $fd nfiles] > 0} { 
		catch {close $fd} err
		return $nfiles
	}
	catch {close $fd} err
	return 0
}

proc do_progress {} \
{
	global	citool errorCode tcl_platform argv

	set citool(nfiles) [nfiles]
	if {$citool(nfiles) > 0} { progress_create "BK citool" 4 }
	set citool(fd) [open "|bk sfiles -vg -xcpA -o$citool(tmp) $argv" "r"]
	set citool(done) 0
	fconfigure $citool(fd) -blocking 0 -buffering none
	fileevent $citool(fd) readable citool_read
	vwait citool(done)
	catch {close $citool(fd)} err
	progress_destroy
	update
}

proc no_progress {} \
{
	global	citool argv

	set fd [open "|bk sfiles -vgxcpC - > $citool(tmp)" "w"]
	foreach a $argv {
		puts $fd "$a"
	}
	close $fd
	set citool(nfiles) 0
}

proc citool { {run "0"} } \
{
	global	argv citool tmp_dir errorCode tcl_platform gc resolve w

	getConfig "ci"

	set run_number 0
	set resolve ""
	if {[lindex $argv 0] == "-R"} {
		set resolve "-R"
		set argv [lreplace $argv 0 0]
	}
	if {("$argv" == "") && ($resolve == "")} { 
		if {[cd2root] == -1} {
			wm withdraw .
			set dir [pwd]
			displayMessage \
			    "Unable to find the project root.\nCurrent directory is $dir" 1
		}
		catch {exec bk sane} err
		if {[lindex $errorCode 2] == 1} {
			wm withdraw .
			displayMessage "$err"
			exit 1
		}
		catch {exec bk clean ChangeSet} err
	}
	if {"$argv" == ""} { set argv . }
	set citool(tmp) [file join $tmp_dir bk_cilist[pid]]
	catch { file delete $citool(tmp) }
	if {[file exists SCCS/s.ChangeSet]} {
		do_progress
	} else {
		wm withdraw .
		no_progress
	}
	if {[file size $citool(tmp)] > 0} {
		incr run_number
		catch {exec bk _sort < $citool(tmp) > $citool(tmp).sorted} err
		if {$err != ""} { puts "error=($err)" }
		file delete $citool(tmp)

		do_citool $citool(tmp).sorted
		vwait citool(exit)
		#puts "citool(exit)=($citool(exit))"
		#tkwait visibility $w(c_upper)
		#puts "after tkwait"

		if {[info exists gc(ci.rescan)] && ($gc(ci.rescan) == 1)} {
			if {$citool(exit) == 200} {
				set a [list]
				if {$resolve != ""} { lappend a $resolve }
				if {"$argv" != ""} { lappend a $argv }
				set argv $a
				citool $run_number
			}
		}
		exit
	} else {
		if {$run == 0} {
			puts "There are no files which require checkin"
		}
		catch { file delete $citool(tmp) }
		exit
	}
}

proc do_citool {file} \
{
	widgets
	getFiles $file
}

bk_init
citool
