# 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
}
# helptool - a tool for showing BK help
# Copyright (c) 1999 by Larry McVoy; All rights reserved
# @(#)helptool.tcl 1.44 lm@work.bitmover.com

#
# Sets the global 'line' to the x'th line after 'line'
#
proc doNext {x} \
{
	global	line nTopics

	if {($x == -1) && ($line == 2.0)} { return }
	if {($x == 1) && ($line == 0.0)} {
		set l 1.0
	} else {
		set l [.ctrl.topics index "$line + $x lines"]
	}
	if {$l > $nTopics} { return }
	set line $l
	doSelect $x
}

proc doNextSection {x} \
{
	global	line nTopics

	if {($x == -1) && ($line == 2.0)} { return }
	if {($x == 1) && ($line == 0.0)} {
		set line 1.0
		doSelect $x
		return
	} 
	set l [.ctrl.topics index "$line + $x lines"]
	if {$l > $nTopics} { return }
	set topic [.ctrl.topics get $l "$l lineend"]
	while {[regexp {^ } $topic]} {
		set l [.ctrl.topics index "$l + $x lines"]
		if {$l > $nTopics} { return }
		set topic [.ctrl.topics get $l "$l lineend"]
	}
	set line $l

	# If going backwards, go back to the first line.
	if {$x == -1 && $line != 1.0} {
		set line [.ctrl.topics index "$line + $x lines"]
		set topic [.ctrl.topics get $line "$line lineend"]
		while {[regexp {^ } $topic]} {
			set line [.ctrl.topics index "$line + $x lines"]
			set topic [.ctrl.topics get $line "$line lineend"]
		}
		.ctrl.topics see $line
	}
	doNext 1
}

proc doPixSelect {x y} \
{
	global	line stackMax stackPos

	set stackMax $stackPos
	set line [.ctrl.topics index "@$x,$y linestart"]
	doSelect 1
}

#
# Selects the x'th line, tags it as selected, calls bkhelp for that topic
#
proc doSelect {x} \
{
	global	line stack stackMax stackPos

	busy 1
	.ctrl.topics see $line
	set topic [.ctrl.topics get $line "$line lineend"]
	if {$topic == ""} { busy 0; return }
	if {[regexp {^ } $topic] == 0} {
		doNext $x
		return
	}
	.ctrl.topics tag remove "select" 1.0 end
	.ctrl.topics tag add "select" $line "$line lineend + 1 char"
	.ctrl.topics tag raise "select"
	bkhelp $topic
	# Don't increment if we are going to where we are
	if {$stackPos == 0 || $stack($stackPos) != $line} { incr stackPos }
	set stack($stackPos) $line
	if {$stackMax < $stackPos} { set stackMax $stackPos }
	if {$stackPos > 1} {
		.menu.back configure -state normal
	} else {
		.menu.back configure -state disabled
	}
	if {$stackMax > $stackPos} {
		.menu.forw configure -state normal
	} else {
		.menu.forw configure -state disabled
	}
	busy 0
}

# set the top of stack to where we are now, we are heading in a new direction.
proc stackReset {} \
{
	global	stackMax stackPos

	set stackMax $stackPos
}

# Pop up the stack one if we can.
proc upStack {} \
{
	global	line stack stackMax stackPos

	if {$stackPos > 1} {
		incr stackPos -1
		set line $stack($stackPos)
		# because doSelect will push again
		incr stackPos -1
		doSelect 1
	}
}

proc downStack {} \
{
	global	line stack stackMax stackPos

	if {$stackMax > $stackPos} {
		incr stackPos
		set line $stack($stackPos)
		# because doSelect will push again
		incr stackPos -1
		doSelect 1
	}
}

proc bkhelp {topic} \
{
	global line line2full gc

	set msg "BitKeeper help -- $topic"
	wm title . $msg
	set f [open "| bk help $gc(help.helptext) -p $topic"]
	.text.help configure -state normal
	.text.help delete 1.0 end
	set lineno 1
	while {[gets $f help] >= 0} {
		.text.help insert end "$help\n"
		if {[regexp {^[A-Z][ \t\nA-Z.?\-\|]+$} $help]} {
			set i "$lineno.0"
			.text.help tag add "bold" $i "$i lineend"
		}
		incr lineno
	}
	.text.help tag add "bold" 2.0 "2.0 lineend + 1 char"
	set i "$lineno.0"
	.text.help tag add "bold" "$i - 2 lines" end
	catch {close $f} dummy
	.text.help configure -state disabled
	bk_highlight
}

proc highlight {tag index len} \
{
	set index2 [.text.help index "$index + $len chars"]
	.text.help tag add $tag $index $index2
	.text.help tag add seealso $index $index2
	.text.help tag bind \
	    $tag <Button-1> "getSelection $tag; stackReset; doSelect 1"
}

# Look for each <bk> word, then find the next word,
# if next_word == help|helptool
#	if word is a topic
#		highlight that word
#	else
#		highlight help|helptool
# else
#	if word is a topic
#		highlight that word
#	else
#		highlight "bk"
# XXX - maybe recode this in C as an option to "bk help"?
proc bk_highlight {} \
{
	set index 4.0
	set t .text.help
	while {"$index" != ""} {
		set index [$t search \
		    -count bklen -regexp {(^| |`|"|/)bk([ ]+|$)} $index end]
		if {"$index" == ""} { break }

		# Get start of "bk"
		if {[$t get $index] == " "} {
			set bkindex [$t index "$index + 1 chars"]
		} else {
			set bkindex $index
		}

		# skip past " bk ".
		set index [$t index "$index + $bklen chars"]

		# Get next word
		set w1index [$t search \
		    -count w1len -regexp {[a-zA-Z0-9_\-]+} $index end]
		if {"$w1index" == ""} {
			highlight bk $bkindex 2
			break
		}
		set w1 [$t get $w1index [$t index "$w1index + $w1len chars"]]
#puts "WORD1=$w1 @ $w1index .. $w1len"
		if {[regexp {^(help|helptool)$} $w1]} {
			# skip past "$w1".
			set index [$t index "$w1index + $w1len chars"]

			# Get next word
			set w2index [$t search \
			    -count w2len -regexp {[a-zA-Z0-9_\-]+} $index end]
			if {"$w2index" == ""} {
				highlight $w1 $w1index $w1len
				break
			}
			set w2 [$t get \
			    $w2index [$t index "$w2index + $w2len chars"]]
#puts "WORD2=$w2 @ $w2index .. $w2len"
			if {[topic2line $w2] != ""} {
				highlight $w2 $w2index $w2len
				set index [$t index "$w2index + $w2len chars"]
			} else {
				highlight $w1 $w1index $w1len
				set index [$t index "$w1index + $w1len chars"]
			}
			continue
		}

		if {[topic2line $w1] != ""} {
			highlight $w1 $w1index $w1len
			set index [$t index "$w1index + $w1len chars"]
		} else {
			highlight bk $bkindex 2
			set index [$t index "$bkindex + 2 chars"]
		}
	}
}

proc topic2line {key} \
{
	global lines aliases full

	set l ""
	catch { set l $lines($key) } dummy
	if {"$l" == ""} { catch { set l $lines($aliases($key)) } dummy }
	if {"$l" != ""} { set l "$l.0" }
	return $l
}

proc search {} \
{
	global	search_word lines gc opts

	if {$search_word == "" } {
		set search_word \
		    "REPLACE all of this with a word, and click search"
		update
		return
	}
	if ($gc(help.exact)) {
		set opts ""
	} else {
		set opts " -a"
	}
	.ctrl.topics tag remove "select" 1.0 end
	.ctrl.topics tag remove "search" 1.0 end
	.text.help configure -state normal
	.text.help delete 1.0 end
	set f [open "| bk helpsearch $gc(help.helptext)$opts -l $search_word" "r"]
	set last ""
	while {[gets $f line] >= 0} {
		set tab [string first "\t" $line"]
		set key [string range $line 0 [expr {$tab - 1}]]
		incr tab
		set sentence [string range $line $tab end]
		set sentence [string trim $sentence]
		if {$last != $key} {
			set last $key
			.text.help insert end "$key\n" "$key seealso"
			.text.help tag bind $key <Button-1> \
			    "getSelection $key; stackReset; doSelect 1; break"
			set l [topic2line $key]
			if {"$l" != ""} {
				.ctrl.topics tag add "search" \
				    "$l linestart" "$l lineend + 1 char"
			}
		}
		.text.help insert end "  $sentence\n"
	}
	catch {close $f} dummy
	.text.help configure -state disabled
	.text.help tag configure seealso -foreground $gc(help.linkColor) \
	    -underline true
}

proc clearSearch {} \
{
	global search_word

	set search_word ""
	.ctrl.topics tag remove "search" 1.0 end
	.text.help configure -state normal
	.text.help delete 1.0 end
	.text.help configure -state disabled
	doSelect 1
}

proc scroll {what dir} \
{
	global	gc line

	set a [lindex [.text.help yview] 0]
	set b [lindex [.text.help yview] 1]
	if {$dir == 1 && $b == 1} {
		doNext 1
	} elseif {$dir == -1 && $a == 0 && $line > 1.0} {
		doNext -1
	} elseif {$what == "page"} {
		set x [expr $gc(help.height) - 1]
		set x [expr $x * $dir]
		.text.help yview scroll $x units
	} else {
		.text.help yview scroll $dir units
	}
}

proc widgets {} \
{
	global	line gc firstConfig pixelsPerLine tcl_platform
	global	search_word stackMax stackPos d

	set stackMax 0
	set stackPos 0

	if {$tcl_platform(platform) == "windows"} {
		set py 0
	} else {
		set py 1
	}
	getConfig "help"
	if {"$gc(help.geometry)" != ""} { wm geometry . $gc(help.geometry) }
	option add *background $gc(BG)

	set rootX [winfo screenwidth .]
	set rootY [winfo screenheight .]
	wm title . "BitKeeper Help"
	set firstConfig 1

	frame .menu -borderwidth 0 -relief flat
	    button .menu.done -text "Quit" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-command { exit }
	    button .menu.help -text "Help" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-command {
			global	line

			clearSearch
			set line [topic2line helptool]
			doSelect 1
		}
	    button .menu.back -text "Back" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-state disabled -command { upStack }
	    button .menu.forw -text "Forw" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-state disabled -command { downStack }
	    button .menu.clear -text "Clear search" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-command { clearSearch }
	    button .menu.search -text "Search:" -font $gc(help.buttonFont) \
		-borderwid 1 -pady $py -background $gc(help.buttonColor) \
		-command { search }
	    entry .menu.entry -font $gc(help.fixedBoldFont) -borderwid 1 \
		-background $gc(help.textBG) -fg $gc(help.textFG) \
		-relief sunken -textvariable search_word
	    grid .menu.done -row 0 -column 0 -sticky ew
	    grid .menu.help -row 0 -column 1 -sticky ew
	    grid .menu.back -row 0 -column 3 -sticky ew
	    grid .menu.forw -row 0 -column 4 -sticky ew
	    grid .menu.clear -row 0 -column 5 -sticky ew
	    grid .menu.search -row 0 -column 6 -sticky ew
	    grid .menu.entry -row 0 -column 7 -sticky ew
	    grid columnconfigure .menu 7 -weight 1
	frame .ctrl -borderwidth 0 -relief flat
	    text .ctrl.topics \
		-spacing1 1 \
		-spacing3 1 \
		-wrap none \
		-height $gc(help.height) \
		-font $gc(help.fixedFont) -width 14 \
		-background $gc(help.listBG) \
		-yscrollcommand [list .ctrl.yscroll set] \
		-xscrollcommand [list .ctrl.xscroll set]
	    scrollbar .ctrl.yscroll \
		-width $gc(help.scrollWidth) \
		-command ".ctrl.topics yview" \
		-background $gc(help.scrollColor) \
		-troughcolor $gc(help.troughColor)
	    scrollbar .ctrl.xscroll \
		-troughcolor $gc(help.troughColor) \
		-background $gc(help.scrollColor) \
		-orient horiz \
		-width $gc(help.scrollWidth) \
		-command ".ctrl.topics xview"

	    grid .ctrl.topics -row 0 -column 0 -sticky nsew
	    grid .ctrl.yscroll -row 0 -column 1 -sticky nse
	    grid .ctrl.xscroll -row 1 -column 0 -columnspan 2 -sticky ew
	    grid rowconfigure .ctrl 0 -weight 1

	frame .text -borderwidth 0 -relief flat
	    text .text.help \
		-wrap none \
		-font $gc(help.fixedFont) \
		-width $gc(help.width) \
		-height $gc(help.height) \
		-padx 4 \
		-background $gc(help.textBG) -fg $gc(help.textFG) \
		-xscrollcommand [list .text.x2scroll set] \
		-yscrollcommand [list .text.y2scroll set]
	    scrollbar .text.x2scroll \
		-orient horiz \
		-troughcolor $gc(help.troughColor) \
		-background $gc(help.scrollColor) \
		-width $gc(help.scrollWidth) \
		-command ".text.help xview"
	    scrollbar .text.y2scroll \
		-width $gc(help.scrollWidth) \
		-troughcolor $gc(help.troughColor) \
		-background $gc(help.scrollColor) \
		-command ".text.help yview"

	    grid .text.help -row 0 -column 1 -sticky nsew
	    grid .text.y2scroll -row 0 -column 0 -sticky nse
	    grid .text.x2scroll -row 1 -column 0 -sticky ew -columnspan 2

	    grid rowconfigure .text 0 -weight 1
	    grid columnconfigure .text 0 -weight 0
	    grid columnconfigure .text 1 -weight 1

	grid .menu -row 0 -column 0 -columnspan 2 -sticky nsew
	grid .ctrl -row 1 -column 0 -sticky nsew
	grid .text -row 1 -column 1 -sticky nsew

	grid rowconfigure . 1 -weight 1
	grid columnconfigure . 0 -weight 0
	grid columnconfigure . 1 -weight 1

	bind .ctrl.topics <Button-1>	{ doPixSelect %x %y; break }
	bind .ctrl.topics <Button-2>	{ doPixSelect %x %y; break }
	bind .ctrl.topics <Button-3>	{ doPixSelect %x %y; break }
	bind .ctrl.topics <Motion>	"break"
	bind .text.help <ButtonPress>	"focus .text.help"
	bind all <Control-e>		{ scroll "line" 1 }
	bind all <Control-y>		{ scroll "line" -1 }
	bind all <Down>			{ scroll "line" 1; break }
	bind all <Up>			{ scroll "line" -1; break }
	bind .text.help <Down>		{ scroll "line" 1; break }
	bind .text.help <Up>		{ scroll "line" -1; break }
	bind all <Left>			".text.help xview scroll -1 units;break"
	bind all <Right>		".text.help xview scroll 1 units; break"
	bind all <Prior>		{ scroll "page" -1; break }
	bind all <Next>			{ scroll "page" 1; break }
	bind Text <Prior>		{ scroll "page" -1; break }
	bind Text <Next>		{ scroll "page" 1; break }
	bind all <Home>		 	".text.help yview -pickplace 1.0; break"
	bind all <End>		 	".text.help yview -pickplace end; break"
	bind all <Control-Up>		{ doNext -1 }
	bind all <Control-Down>		{ doNext 1 }
	bind all <Control-Left>	 	"doNextSection -1"
	bind all <Control-Right> 	"doNextSection 1"
	bind all <Alt-Left>		{ upStack }
	bind all <Alt-Right>		{ downStack }
	bind all <$gc(help.quit)>	{ exit }
	if {$tcl_platform(platform) == "windows"} {
		bind all <MouseWheel> {
		    if {%D < 0} {
			scroll "page" 1
		    } else {
			scroll "page" -1
		    }
		}
	} else {
		bind all <Button-4> 	{ scroll "page" -1; break }
		bind all <Button-5> 	{ scroll "page" 1; break }
	}
	bind .menu.entry <Return> { search }
	bindtags .menu.entry { all .menu.entry Entry . }
 	bindtags .ctrl.topics {.ctrl.topics . all}
	#bindtags .text.help {.text.help . all}
	bind .text.help <Configure> {
		global	gc pixelsPerLine firstConfig

		set x [winfo height .text.help]
		# This gets executed once, when we know how big the text is
		if {$firstConfig == 1} {
			set h [winfo height .text.help]
			set pixelsPerLine [expr {$h / $gc(help.height)}]
			set firstConfig 0
		}
		set x [expr {$x / $pixelsPerLine}]
		set gc(help.height) $x
	}
	.ctrl.topics tag configure "select" -background $gc(help.selectColor) \
	    -relief ridge -borderwid 1
	.text.help tag configure "title" -background #8080c0
	.text.help tag configure "bold" -font $gc(help.fixedBoldFont)
	.text.help tag configure seealso -foreground $gc(help.linkColor) \
	    -underline true
	.ctrl.topics tag configure "search" -background $gc(help.topicsColor) \
	    -relief ridge -borderwid 1

	. configure -background $gc(BG)
	wm deiconify .
	focus .menu.entry
}

proc busy {busy} \
{
	if {$busy != 0} {
		. configure -cursor watch
		.text.help configure -cursor watch
		.ctrl.topics configure -cursor watch
	} else {
		. configure -cursor left_ptr
		.text.help configure -cursor left_ptr
		.ctrl.topics configure -cursor left_ptr
	}
	update
}

proc getSelection {argv} \
{
	global line lines aliases

	set l ""
	catch { set l $lines($argv) } dummy
	if {"$l" == ""} { catch { set l $lines($aliases($argv)) } dummy }
	if {"$l" == ""} {
		puts "No help for $argv"
		exit
	}
	incr l -1
	set line [.ctrl.topics index "1.0 + $l lines"]
}

proc getHelp {} \
{
	global	nTopics argv line lines aliases line2full gc

	set nTopics 0
	set file [lindex $argv 0]
	if {[regexp {^-f} $file]} {
		if {$file == "-f"} {
			set file [lindex $argv 1]
			set gc(help.helptext) "-f$file"
			set keyword [lindex $argv 2]
		} else {
			set gc(help.helptext) $file
			set keyword [lindex $argv 1]
		}
	} else {
		set keyword [lindex $argv 0]
	}
	set f [open "| bk helptopics $gc(help.helptext)"]
	.ctrl.topics configure -state normal
	set section ""
	while {[gets $f topic] >= 0} {
		if {$topic == "Aliases"} { break }
		.ctrl.topics insert end "$topic\n"
		if {[string index $topic 0] != " "} {
			set section $topic
			set topic ""
		} else {
			set topic [string trim $topic]
		}
		incr nTopics 1
		set lines($topic) $nTopics
		if {$topic != ""} {
			set full "$section/$topic"
		} else {
			set full $section
		}
		set lines($full) $nTopics
		set index "$nTopics.0"
		set line2full($index) $full
	}
	while {[gets $f topic] >= 0} {
		set l [split $topic \t]
		set key [lindex $l 0]
		set val [lindex $l 1]
		set aliases($key) $val
		# Store the short key as well.  This can cause problems
		# if there are name space collisions, but then use full names.
		set slash [string first "/" $key]
		incr slash
		set short [string range $key $slash end]
		set aliases($short) $val
	}
	catch {close $f} dummy
	.ctrl.topics configure -state disabled
	.text.help configure -state disabled
	if {$keyword != ""} {
		getSelection $keyword
	} else {
		set line 1.0
	}
	doSelect 1
}

bk_init
widgets
getHelp
