wm withdraw .
# Copyright 1999-2004,2008-2010,2013 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

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

proc bk_initPlatform {} \
{
	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 "bk" "ndiff" "--sdiff=1" "--ignore-trailing-cr"]
	set dev_null "/dev/null"
	set wish "wish"
	set tmp_dir  "/tmp"
	if {[info exists env(TMPDIR)] && [file writable $env(TMPDIR)]} {
		set tmp_dir $env(TMPDIR)
	}
	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"
	catch { unset env(BK_NO_GUI_PROMPT) }

	# Determine the bk icon to associate with toplevel windows. If
	# we can't find the icon, don't set the global variable. This
	# way code that needs the icon can check for the existence of
	# the variable rather than checking the filesystem.
	set f [file join [exec bk bin] bk.xbm]
	if {[file exists $f]} {
		set ::wmicon $f
		# N.B. on windows, wm iconbitmap supports a -default option
		# that is not available on unix. Bummer. 
		catch {wm iconbitmap . @$::wmicon}
	}
}
# Copyright 2000-2013,2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

proc gc {var {newValue ""}} \
{
	global	gc app

	## If we got a config var with no APP. prefix, check to see if
	## we have an app-specific config option first and force them
	## to use that if we do.
	if {[info exists app]
	    && ![string match "*.*" $var]
	    && [info exists gc($app.$var)]} {
		set var $app.$var
	}
	if {[llength [info level 0]] == 3} { set gc($var) $newValue }
	return $gc($var)
}

## An array of deprecated options and the warning to display to the
## user if they are found to be using said option.
array set deprecated {
"rev.showHistory"

"The rev.showHistory config option has been removed.
Please see 'bk help config-gui' for rev.showRevs and
rev.showCsetRevs for new options."
}

proc warn_deprecated_options {app} \
{
	global	gc deprecated

	foreach {opt desc} [array get deprecated $app.*] {
		if {![info exists gc($opt)]} { continue }
		puts ""
		foreach line [split $desc \n] {
			puts [string trim $line]
		}
	}
}

proc getConfig {prog} \
{
	global gc app env usergc

	# this causes variables like _RED_, _WHITE_, to exist in this proc
	defineSymbolicColors

	set app $prog

	option add *Label.borderWidth 1 100
	option add *Button.borderWidth 1 100
	option add *Menubutton.borderWidth 1 100
	option add *Menu.tearOff 0 widgetDefault
	option add *Menu.borderWidth 1 widgetDefault
	option add *Menu.highlightThickness 1 widgetDefault
	option add *Menu.activeBorderWidth 1 widgetDefault

	initFonts $app _d

	# colorscheme

	set _d(classicColors) 1		;# default to the old color scheme.

	set _d(tabwidth) 8		;# default width of a tab
	set _d(backup) ""		;# Make backups in ciedit: XXX NOTDOC 
	set _d(buttonColor) $SYSTEMBUTTONFACE	;# menu buttons
	set _d(diffOpts) ""		;# options to pass to diff
	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) $GRAY91		;# topics / lists background
	set _d(mergeHeight) 24		;# height of a merge window
	set _d(mergeWidth) 80		;# width of a merge window
	set _d(newColor) #c4d7c5	;# color of new revision/diff
	set _d(noticeColor) #dbdfe6	;# messages, warnings
	set _d(oldColor) $GRAY88	;# color of old revision/diff
	set _d(searchColor) $ORANGE	;# highlight for search matches
	set _d(selectColor) $LIGHTBLUE	;# current file/item/topic
	set _d(statusColor) $LIGHTBLUE	;# various status windows
	set _d(minsize) 300		;# won't remember geometry if smaller
					 # than this width or height
	#XXX: Not documented yet
	set _d(logoBG) $WHITE		;# background for widget with logo
	set _d(selectBG) $NAVY		;# useful for highlighting text
	set _d(selectFG) $WHITE		;# useful for highlighting text
	set _d(altColumnBG) $BEIGE		;# alternate column background
	set _d(infoColor) $LIGHTYELLOW	;# color of info line in difflib
	set _d(textBG) $WHITE			;# text background
	set _d(textFG) $BLACK			;# text color
	set _d(scrollColor) $GRAY85		;# scrollbar bars
	set _d(troughColor) $LIGHTBLUE	;# scrollbar troughs
	set _d(warnColor) yellow		;# error messages
	set _d(emptyBG) black
	set _d(sameBG) white
	set _d(spaceBG) white
	set _d(changedBG) gray

	set _d(quit)	Control-q	;# binding to exit tool
	set _d(compat_4x) 0		;# maintain compatibility with 4x
					;# quirky bindings
	set _d(highlightOld) $YELLOW2	;# subline highlight color for old
	set _d(highlightNew) $YELLOW2	;# subline highlight color for new
	set _d(highlightsp) $ORANGE	;# subline highlight color in diffs
	set _d(topMargin) 2		;# top margin for diffs in a diff view
	set _d(diffColor) #ededed	;# color of diff lines
	set _d(activeDiffColor) $BKGREEN1 ;# active diff color
	set _d(activeOldColor) $_d(activeDiffColor)
	set _d(activeNewColor) $_d(activeDiffColor)
	set _d(newFont) bkFixedFont
	set _d(oldFont) bkFixedFont
	set _d(activeOldFont) bkFixedFont
	set _d(activeNewFont) bkFixedFont

	set _d(bug.popupBG) $BLUE
	set _d(support.popupBG) $BLUE
	set _d(ci.iconBG) $BKPALEOLIVE	;# background of some icons
	set _d(ci.csetIconBG) $BKBLUE1	;# background of some icons
	set _d(ci.quitSaveBG) $BKSLATEBLUE1	;# "quit but save" button
	set _d(ci.quitSaveActiveBG) $BKSLATEBLUE2	;# "quit but save" button
	set _d(ci.listBG) white	
	set _d(selectColor) #f0f0f0	;# current file/item/topic
	set _d(ci.saveBG) $GRAY94		;# background of save dialog
	set _d(ci.quitNosaveBG) $RED	;# "don't save" button
	set _d(ci.quitNosaveActiveBG) $WHITE ;# "don't save" button
	set _d(ci.dimFG) $GRAY50		;# dimmed text
	set _d(ci.progressBG) $WHITE		;# background of progress bar
	set _d(ci.progressColor) $BKSLATEBLUE1 ;# color of progress bar
	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.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(ci.csetBG) $GRAY94

	set _d(cset.listHeight) 12
	set _d(cset.annotation) ""   ;# annotation options (eg: "-aum")
	set _d(cset.doubleclick) 100 ;# XXX: NOTDOC

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

	set _d(fm.redoBG) $PINK
	set _d(fm3.conflictBG) gray		;# Color for conflict message
	set _d(fm3.unmergeBG) gray	;# Color for unmerged message
	set _d(fm3.annotate) 1		;# show annotations
	set _d(fm3.comments) 1		;# show comments window
	set _d(fm3.escapeButtonFG) $YELLOW	;# foreground of escape button
	set _d(fm3.escapeButtonBG) $BLACK	;# background of escape button
	set _d(fm3.firstDiff) minus
	set _d(fm3.lastDiff) plus
	set _d(fm3.mergeColor) #b4b6cb	;# color of merge choices in merge win
	set _d(fm3.handColor) $_d(fm3.mergeColor) ;# color of hand merged choices
	set _d(fm3.nextConflict) braceright
	set _d(fm3.nextDiff) bracketright
	set _d(fm3.prevConflict) braceleft
	set _d(fm3.prevDiff) bracketleft
	set _d(fm3.sameColor) #efefef	;# color of unchanged line
	set _d(fm3.showEscapeButton) 1	;# show escape button?
	set _d(fm3.spaceColor) $BLACK	;# color of spacer lines
	set _d(fm3.toggleGCA) x		;# key to toggle GCA info
	set _d(fm3.toggleAnnotations) z	;# key to toggle annotations
	set _d(fm3.undo) u
	set _d(fm3.animateScrolling) 1	;# Use an animated scrolling effect
					;# when jumping conflict diff blocks.

	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) 79		;# number of columns to display
	set _d(help.helptext) ""	;# -f<helptextfile> - undocumented
	set _d(help.exact) 0		;# helpsearch, allows partial matches
	set _d(help.scrollbars) RR	;# sides for each scrollbar

	set _d(rename.listHeight) 8

	# N.B. 500ms is the hard-coded constant in tk used to detect
	# double clicks. We need a number slightly larger than that. The
	# book Practical Programming in Tcl/Tk, 4th ed. recommends 600. This
	# results in a noticable delay before a single-click is processed 
	# but there really is no other solution when a double-click must
	# override a single click, or a single-click action will take more
	# than 500ms and therefore preventing double-clicks from ever being
	# noticed.
	set _d(rev.doubleclick) 600  ;# XXX: NOTDOC
	set _d(rev.sashBG) $BLACK
	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) $BKCADETBLUE	;# unselected box fills
	set _d(rev.localColor) $GREEN	;# local node (for resolve)
	set _d(rev.remoteColor) $RED	;# remote node (for resolve)
	set _d(rev.gcaColor) $WHITE		;# gca node (for resolve)
	set _d(rev.tagOutline) $YELLOW	;# outline of tagged nodes
	set _d(rev.badColor) $RED		;# color for "bad" revision numbers
	set _d(rev.selectColor) $BKSTEELBLUE ;# highlight color for selected tag
	set _d(rev.dateLineColor) $LIGHTBLUE ;# line that separates dates
	set _d(rev.dateColor) $BKBLACK1	;# 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.showRevs) 250	  ;# Num of revs to show in graph 
	set _d(rev.showCsetRevs) 50	  ;# Num of revs to show for a cset
	# 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.annotate) "-Aur"	  ;# Options given to annotate

	set _d(setup.stripeColor) $BLUE ;# color of horizontal separator
	set _d(setup.mandatoryColor) $BKSLATEGRAY1 ;# mandatory fields
	set _d(bug.mandatoryColor) $BKSLATEGRAY1 ;# mandatory fields
	set _d(support.mandatoryColor) $BKSLATEGRAY1 ;# mandatory fields
	set _d(entryColor) $WHITE	   ;# Color of input fields

	set _d(search.width)		15
	set _d(search.buttonWidth)	15

	set _d(ignoreWhitespace)	0
	set _d(ignoreAllWhitespace)	0

	set _d(hlPercent)	0.5
	set _d(chopPercent)	0.5

	set _d(windows) 0
	set _d(aqua) 0
	set _d(x11) 0

	switch -exact -- [tk windowingsystem] {
	    win32 {
		set _d(windows) 1
		set _d(handCursor) "hand2"
		set _d(cset.leftWidth) 40
		set _d(cset.rightWidth) 80
		set _d(ci.filesHeight) 8
		set _d(ci.commentsHeight) 7	;# height of comment window
		set _d(buttonColor) $SYSTEMBUTTONFACE	;# menu buttons
		set _d(BG) $SYSTEMBUTTONFACE		;# default background
		# usable space
		set _d(padTop)		0
		set _d(padBottom)	28		; # someday, we'll 
							  # need to figure
							  # out the size
							  # of the taskbar
		set _d(padRight)	0
		set _d(padLeft)		0
		set _d(titlebarHeight)	20
		set rcfile [exec bk dotbk _bkgui config-gui]
	    } 
	    aqua {
		set _d(aqua) 1
		set _d(handCursor) "pointinghand"
		set _d(cset.leftWidth) 40
		set _d(cset.rightWidth) 80
		set _d(search.width) 4
		set _d(search.buttonWidth) 12
		set _d(ci.filesHeight) 8
		set _d(ci.commentsHeight) 7	;# height of comment window
		set _d(buttonColor) $SYSTEMBUTTONFACE	;# menu buttons
		set _d(BG) $SYSTEMBUTTONFACE		;# default background
		set _d(listBG) $WHITE
		#usable space
		set _d(padTop)		22              ; # someday we'll need
							  # to compute the
							  # size of the menubar
		set _d(padBottom)	0
		set _d(padRight)	0
		set _d(padLeft)		0
		set _d(titlebarHeight)	22
		set rcfile [exec bk dotbk .bkgui config-gui]
	    }
	    x11 {
		set _d(x11) 1

		option add *Scrollbar.borderWidth 1 100
		set _d(handCursor) "hand2"
		set _d(cset.leftWidth) 55
		set _d(cset.rightWidth) 80
		set _d(scrollWidth) 12		;# scrollbar width
		set _d(ci.filesHeight) 9	;# num files to show in top win
		set _d(ci.commentsHeight) 8	;# height of comment window
		set _d(fm.editor) "fm2tool"
		set _d(buttonColor) $SYSTEMBUTTONFACE	;# menu buttons
		set _d(BG) $GRAY85		;# default background
		# usable space (all of it in X11)
		set _d(padTop)		0
		set _d(padBottom)	0
		set _d(padRight)	0
		set _d(padLeft)		0
		set _d(titlebarHeight)	0
		if {$::tcl_platform(os) eq "Darwin"} {
			# We might be in X11 under Aqua, so leave room
			# for the menubar
			set _d(padTop)		22
			set _d(titlebarHeight)	22
		}
		set rcfile [exec bk dotbk .bkgui config-gui]
	    }
	    default {
		puts "Unknown windowing system"
		exit
	    }
	}

	set gc(activeNewOnly) 1

	set gc(bkdir) [file dirname $rcfile]
	if {[file readable $rcfile]} {
		source $rcfile
		warn_deprecated_options $app
	}

	## Save a copy of the gc array exactly as the user specified it.
	array set usergc [array get gc]

	## If the user specified some global option in their config-gui
	## file, write that same value into the app-specific value for
	## the current tool.  This ensures that all values from the user
	## overwrite any values we've set in here.
	foreach var [array names usergc] {
		if {[string match "*.*" $var]} { continue }
		set gc($app.$var) $usergc($var)
	}

	if {[info exists gc(classicColors)]} {
		set _d(classicColors) $gc(classicColors)
	}
	set _d($app.classicColors) $_d(classicColors)
	foreach p [list "" "$prog."] {
		if {[info exists gc(${p}classicColors)]} {
			set _d(${p}classicColors) $gc(${p}classicColors)
		}
	}

	if {[string is true -strict $_d($app.classicColors)]} {
		# set "classic" color scheme. Setting it this way
		# still lets users override individual colors by
		# setting both gc(classicColors) _and_ the color they
		# want to change
		set _d(oldColor) #C8A0FF
		set _d(newColor) #BDEDF5
		set _d(activeOldColor) $_d(oldColor)
		set _d(activeNewColor) $_d(newColor)
		set _d(activeOldFont) bkFixedBoldFont
		set _d(activeNewFont) bkFixedBoldFont
		set _d(noticeColor) $BKBLUE1
		set _d(searchColor) $YELLOW
		set _d(infoColor) $POWDERBLUE
		set _d(warnColor) $YELLOW
		set _d(highlight) $YELLOW2
		set _d(diffColor) $GRAY88
		set _d(activeDiffColor) $BKGREEN1
		set _d(fm3.conflictBG) $RED
		set _d(fm3.unmergeBG) $LIGHTYELLOW
		set _d(fm3.mergeColor) $LIGHTBLUE
		set _d(fm3.handColor) $LIGHTYELLOW
		set _d(fm3.sameColor) $BKTURQUOISE1
	}

	## Set these colors regardless of classic or not.
	set _d(diff.activeOldColor) $_d(activeDiffColor)
	set _d(diff.activeNewColor) $_d(activeDiffColor)
	set _d(diff.activeOldFont) bkFixedFont
	set _d(diff.activeNewFont) bkFixedFont

	set _d(cset.activeOldColor) $_d(activeDiffColor)
	set _d(cset.activeNewColor) $_d(activeDiffColor)
	set _d(cset.activeOldFont) bkFixedFont
	set _d(cset.activeNewFont) bkFixedFont

	set _d(fm.activeOldColor) $_d(activeDiffColor)
	set _d(fm.activeNewColor) $_d(activeDiffColor)
	set _d(fm.activeOldFont) bkFixedFont
	set _d(fm.activeNewFont) bkFixedFont

	## If the user specified showRevs but not showCsetRevs, use showRevs
	## for both values for backward compatibility.
	foreach p [list "" "$prog."] {
		if {[info exists gc(${p}showRevs)]
		    && ![info exists gc(${p}showCsetRevs)]} {
			set _d(${p}showCsetRevs) $gc(${p}showRevs)
		}
	}

	if {$prog eq "fm3"} {
		## For backward compatibility, if the user has specified
		## charColor in fm3tool, we'll use that as our subline
		## highlight color if they haven't also specified a
		## highlight color to use.
		foreach p [list "" "$prog."] {
			if {[info exists gc(${p}charColor)]
			    && ![info exists gc(${p}highlight)]} {
				set _d(${p}highlight) $gc(${p}charColor)
			}
		}
	}

	## If the user specified activeDiffColor but didn't give us anything
	## for activeNewColor or activeOldColor, fill in the activeDiffColor
	## for those values.
	foreach p [list "" "$prog."] {
		if {![info exists gc(${p}activeDiffColor)]} { continue }
		if {![info exists gc(${p}activeOldColor)]} {
			set _d(${p}activeOldColor) $gc(${p}activeDiffColor)
		}
		if {![info exists gc(${p}activeNewColor)]} {
			set _d(${p}activeNewColor) $gc(${p}activeDiffColor)
		}
	}

	# Pass one just copies all the defaults into gc unless they are set
	# already by the config file
	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"
			}
		}
    	}

	if {![info exists gc(rev.graphFont)]} {
		if {$gc(fixedFont) eq "bkFixedFont"} {
			set gc(rev.graphFont) $gc(default.fixedFont)
		} else {
			set gc(rev.graphFont) $gc(fixedFont)
		}
	}
	if {![info exists gc(rev.graphBoldFont)]} {
		if {$gc(fixedBoldFont) eq "bkFixedBoldFont"} {
			set gc(rev.graphBoldFont) $gc(default.fixedBoldFont)
		} else {
			set gc(rev.graphBoldFont) $gc(fixedBoldFont)
		}
	}

	foreach var [array names gc *Font] {
		switch -- $gc($var) {
			"default" {
				set gc($var) bkButtonFont
			}
			"default bold" {
				set gc($var) bkBoldFont
			}
			"fixed" {
				set gc($var) bkFixedFont
			}
			"fixed bold" {
				set gc($var) bkFixedBoldFont
			}
		}
	}

	configureFonts $app

	option add *Text.tabStyle wordprocessor

	if {$gc($app.tabwidth) != 8} {
		option add *Text.tabs \
		    [expr {$gc($app.tabwidth) * [font measure bkFixedFont 0]}]
	}
}

proc initFonts {app var} \
{
	upvar 1 $var _d

	## Twiddle the default Tk fonts more to our liking.
	switch -- [tk windowingsystem] {
		"x11" {
			font configure TkTextFont -size -14
			font configure TkDefaultFont -size -14
		}
		"win32" {
			font configure TkFixedFont -size 8
		}
	}

	set font [font configure TkTextFont]
	set bold [dict replace $font -weight bold]

	set fixed     [font configure TkFixedFont]
	set fixedBold [dict replace $fixed -weight bold]

	set fonts [font names]
	if {"bkBoldFont" ni $fonts} {
		font create bkBoldFont {*}$bold
	}
	if {"bkButtonFont" ni $fonts} {
		font create bkButtonFont {*}$font
	}
	if {"bkNoticeFont" ni $fonts} {
		font create bkNoticeFont {*}$bold
	}
	if {"bkFixedFont" ni $fonts} {
		font create bkFixedFont {*}$fixed
	}
	if {"bkFixedBoldFont" ni $fonts} {
		font create bkFixedBoldFont {*}$fixedBold
	}

	set _d(default.boldFont) $bold
	set _d(default.buttonFont) $font
	set _d(default.noticeFont) $bold
	set _d(default.fixedFont) $fixed
	set _d(default.fixedBoldFont) $fixedBold

	set _d(boldFont)	bkBoldFont
	set _d(buttonFont)	bkButtonFont
	set _d(noticeFont)	bkNoticeFont
	set _d(fixedFont)	bkFixedFont
	set _d(fixedBoldFont)	bkFixedBoldFont

	if {[tk windowingsystem] eq "aqua"} {
		bind all <Command-0>     "adjustFontSizes 0"
		bind all <Command-plus>  "adjustFontSizes 1"
		bind all <Command-equal> "adjustFontSizes 1"
		bind all <Command-minus> "adjustFontSizes -1"
	} else {
		bind all <Control-0>     "adjustFontSizes 0"
		bind all <Control-plus>  "adjustFontSizes 1"
		bind all <Control-equal> "adjustFontSizes 1"
		bind all <Control-minus> "adjustFontSizes -1"
	}
}

proc configureFonts {app} \
{
	global	gc usergc

	set res [getScreenSize]
	foreach font {boldFont buttonFont noticeFont fixedFont fixedBoldFont} {
		set name bk[string toupper $font 0]

		## If they have a saved state for this font, use it.
		## Otherwise we use whatever is configured either from
		## initFonts or potentially from config-gui.
		if {[info exists usergc($font)]} {
			set f $usergc($font)
		} elseif {[info exists ::State($font@$res)]} {
			set f $::State($font@$res)
		} else {
			set f $gc($font)
		}
		if {[string index $f 0] ne "-"} { set f [font actual $f] }
		font configure $name {*}$f
		set gc($font) $name
		set gc($app.$font) $name
	}
}

proc adjustFontSizes {n} \
{
	global	gc

	set res [getScreenSize]
	foreach font {fixedFont fixedBoldFont} {
		set name bk[string toupper $font 0]
		if {$n == 0} {
			set opts $gc(default.$font)
		} else {
			set opts [font configure $name]
			set size [dict get $opts -size]
			if {$size >= 0} {
				if {$size <= 6 && $n < 0} { return }
				dict incr opts -size $n
			} else {
				if {$size >= -6 && $n < 0} { return }
				dict incr opts -size [expr {-$n}]
			}
		}
		font configure $name {*}$opts
		set ::State($font@$res) $opts
	}
}

# At one point, the bk guis used symbolic color names to define some
# widget colors. Experience has shown that some color names aren't
# portable across platforms. Bug <2002-12-09-004> shows that the color 
# "darkblue", for instance, doesn't exist on some platforms.
#
# Hard-coding hex values in getConfig() is more portable, but hard
# to read. Thus, this proc defines varibles which can be used in lieu
# of hex values. The names and their definitions are immutable; if you
# want to change a color in a GUI, don't change it here. Define a new
# symbolic name, or pick an existing symbolic name. Don't redefine an
# existing color name.
proc defineSymbolicColors {} \
{
	uplevel {
		# these are taken from X11's rgb.txt file
		set BEIGE		#f5f5dc
		set BLACK		#000000
		set BLUE		#0000ff
		set DARKBLUE		#00008b
		set GRAY50		#7f7f7f
		set GRAY85		#d9d9d9
		set GRAY88		#e0e0e0
		set GRAY91		#e8e8e8
		set GRAY94		#f0f0f0
		set GREEN		#00ff00
		set LIGHTBLUE		#add8e6
		set LIGHTYELLOW	#ffffe0
		set NAVY		#000080
		set ORANGE		#ffa500
		set PINK		#ffc0cb
		set POWDERBLUE	#b0e0e6
		set RED		#ff0000
		set WHITE		#ffffff
		set YELLOW		#ffff00
		set YELLOW2		#fffd56

		# This is used for menubuttons, and is based on the
		# "SystemButtonFace" on windows. 
		if {[tk windowingsystem] eq "win32"} {
#			set SYSTEMBUTTONFACE #d4d0c8
                        set SYSTEMBUTTONFACE systembuttonface
		} elseif {[tk windowingsystem] eq "aqua"} {
			set SYSTEMBUTTONFACE $WHITE
		} else {
			set SYSTEMBUTTONFACE [ttk::style lookup . -background]
		}

		# these are other colors for which no official name exists;
		# there were once hard-coded into getConfig(), but I've
		# given them symbolic names to be consistent with all of
		# the other colors. I tried to visually match them to a
		# similar color in rgb.txt and added a BK prefix
		set BKBLACK1		#181818
		set BKBLUE1		#b0b0e0
		set BKBLUE2		#a8d8e0
		set BKBLUE3		#b4e0ff
		set BKCADETBLUE		#9fb6b8
		set BKGREEN1		#2fedad
		set BKGREEN2		#a2dec3
		set BKPALEOLIVE		#e8f8a6
		set BKPLUM		#dfafdf
		set BKSLATEBLUE1	#a0a0ff
		set BKSLATEBLUE2	#c0c0ff
		set BKSLATEGRAY1	#deeaf4
		set BKSTEELBLUE		#adb8f6
		set BKTURQUOISE1	#1cc7d0
		set BKVIOLET1		#b48cff
	}
}
# Copyright 2011,2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
namespace eval ttk::theme::bk {

    package provide ttk::theme::bk 0.1

    variable colors ; array set colors {
	-disabledfg	"#999999"

	-frame  	"#EEEEEE"
        -lighter        "#FBFBFB"
        -dark           "#DEDEDE"
        -darker         "#CDCDCD"
        -darkest        "#979797"
        -pressed        "#C5C5C5"
	-lightest 	"#ffffff"
	-selectbg	"#447BCD"
	-selectfg	"#ffffff"

        -activebutton   "#E5E8ED"
    }

    ttk::style theme create bk -parent clam -settings {
	ttk::style configure "." \
	    -background $colors(-frame) \
	    -foreground black \
	    -bordercolor $colors(-darkest) \
	    -darkcolor $colors(-dark) \
	    -lightcolor $colors(-lighter) \
	    -troughcolor $colors(-darker) \
	    -selectbackground $colors(-selectbg) \
	    -selectforeground $colors(-selectfg) \
	    -selectborderwidth 0 \
	    -font TkDefaultFont \
            -indicatorsize 12 \
	    ;

	ttk::style map "." \
	    -background [list disabled $colors(-frame) \
			     active $colors(-lighter)] \
	    -foreground [list disabled $colors(-disabledfg)] \
	    -selectbackground [list !focus $colors(-darkest)] \
	    -selectforeground [list !focus white] \
	    ;

	ttk::style configure TButton \
            -anchor center -width -8 -padding 1 -relief raised
	ttk::style map TButton \
	    -background [list \
			     disabled $colors(-frame) \
			     pressed $colors(-pressed) \
			     active $colors(-activebutton)] \
	    -lightcolor [list \
                            disabled $colors(-lighter) \
                            pressed "#C5C5C5" \
                            active  "#A9C1E7"] \
	    -darkcolor [list \
                            disabled $colors(-dark) \
                            pressed "#D6D6D6" \
                            active "#7BA1DA"] \
	    ;

	ttk::style configure Toolbutton -anchor center -padding 1 -relief flat
	ttk::style map Toolbutton \
	    -relief [list \
                        disabled flat \
                        selected sunken \
                        pressed  sunken \
                        active   raised] \
	    -background [list \
			     disabled $colors(-frame) \
			     pressed $colors(-pressed) \
			     active $colors(-activebutton)] \
	    -lightcolor [list \
                            pressed "#C5C5C5" \
                            active  "#A9C1E7"] \
	    -darkcolor [list \
                            pressed "#D6D6D6" \
                            active "#7BA1DA"] \
	    ;

	ttk::style configure TCheckbutton \
	    -indicatorbackground "#ffffff" -indicatormargin {1 1 4 1}
	ttk::style configure TRadiobutton \
	    -indicatorbackground "#ffffff" -indicatormargin {1 1 4 1}
	ttk::style map TCheckbutton -indicatorbackground \
	    [list  disabled $colors(-frame)  pressed $colors(-frame)]
	ttk::style map TRadiobutton -indicatorbackground \
	    [list  disabled $colors(-frame)  pressed $colors(-frame)]

	ttk::style configure TMenubutton \
            -width -8 -padding 1 -relief raised

	ttk::style configure TEntry -padding 1 -insertwidth 1
	ttk::style map TEntry \
	    -background [list  readonly $colors(-frame)] \
	    -bordercolor [list  focus $colors(-selectbg)] \
	    -lightcolor [list  focus #6490D2] \
	    -darkcolor [list  focus #60A0FF]

	ttk::style configure TCombobox -padding 1 -insertwidth 1
	ttk::style map TCombobox \
	    -background [list active $colors(-lighter) \
			     pressed $colors(-lighter)] \
	    -fieldbackground [list {readonly focus} $colors(-selectbg)] \
	    -foreground [list {readonly focus} $colors(-selectfg)] \
	    ;

	ttk::style configure TNotebook.Tab -padding {6 0 6 2}
	ttk::style map TNotebook.Tab \
	    -padding [list selected {6 2 6 2}] \
	    -background [list active $colors(-activebutton)] \
	    -lightcolor [list \
                active #6490D2 selected $colors(-lighter) {} $colors(-dark) \
            ] \
	    -darkcolor  [list active #60A0FF] \
	    ;

    	ttk::style configure TLabelframe \
	    -labeloutside true -labelinset 0 -labelspace 2 \
	    -borderwidth 2 -relief raised

	ttk::style configure TProgressbar -background #437BCC \
            -darkcolor #546F98 -lightcolor #467ED7

	ttk::style configure Sash -sashthickness 6 -gripcount 10
    }
}
# tooltip.tcl --
#
#       Balloon help
#
# Copyright (c) 1996-2007 Jeffrey Hobbs
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# 
# RCS: @(#) $Id: tooltip.tcl,v 1.16 2008/12/01 23:37:16 hobbs Exp $
#
# Initiated: 28 October 1996


package require Tk 8.4
package require msgcat

#------------------------------------------------------------------------
# PROCEDURE
#	tooltip::tooltip
#
# DESCRIPTION
#	Implements a tooltip (balloon help) system
#
# ARGUMENTS
#	tooltip <option> ?arg?
#
# clear ?pattern?
#	Stops the specified widgets (defaults to all) from showing tooltips
#
# delay ?millisecs?
#	Query or set the delay.  The delay is in milliseconds and must
#	be at least 50.  Returns the delay.
#
# disable OR off
#	Disables all tooltips.
#
# enable OR on
#	Enables tooltips for defined widgets.
#
# <widget> ?-index index? ?-items id? ?-tag tag? ?message?
#	If -index is specified, then <widget> is assumed to be a menu
#	and the index represents what index into the menu (either the
#	numerical index or the label) to associate the tooltip message with.
#	Tooltips do not appear for disabled menu items.
#	If -item is specified, then <widget> is assumed to be a listbox
#	or canvas and the itemId specifies one or more items.
#	If -tag is specified, then <widget> is assumed to be a text
#	and the tagId specifies a tag.
#	If message is {}, then the tooltip for that widget is removed.
#	The widget must exist prior to calling tooltip.  The current
#	tooltip message for <widget> is returned, if any.
#
# RETURNS: varies (see methods above)
#
# NAMESPACE & STATE
#	The namespace tooltip is used.
#	Control toplevel name via ::tooltip::wname.
#
# EXAMPLE USAGE:
#	tooltip .button "A Button"
#	tooltip .menu -index "Load" "Loads a file"
#
#------------------------------------------------------------------------

namespace eval ::tooltip {
    namespace export -clear tooltip
    variable labelOpts
    variable tooltip
    variable G

    if {![info exists G]} {
        array set G {
            enabled     1
            fade        1
            FADESTEP    0.2
            FADEID      {}
            DELAY       500
            AFTERID     {}
            LAST        -1
            TOPLEVEL    .__tooltip__
        }
        if {[tk windowingsystem] eq "x11"} {
            set G(fade) 0 ; # don't fade by default on X11
        }
    }
    if {![info exists labelOpts]} {
	# Undocumented variable that allows users to extend / override
	# label creation options.  Must be set prior to first registry
	# of a tooltip, or destroy $::tooltip::G(TOPLEVEL) first.
	set labelOpts [list -highlightthickness 0 -relief solid -bd 1 \
			   -background lightyellow -fg black]
    }

    # The extra ::hide call in <Enter> is necessary to catch moving to
    # child widgets where the <Leave> event won't be generated
    bind Tooltip <Enter> [namespace code {
	#tooltip::hide
	variable tooltip
	variable G
	set G(LAST) -1
	if {$G(enabled) && [info exists tooltip(%W)]} {
	    set G(AFTERID) \
		[after $G(DELAY) [namespace code [list _show %W $tooltip(%W) cursor]]]
	}
    }]

    bind Menu <<MenuSelect>>	[namespace code { menuMotion %W }]
    bind Tooltip <Leave>	[namespace code [list hide 1]] ; # fade ok
    bind Tooltip <Any-KeyPress>	[namespace code hide]
    bind Tooltip <Any-Button>	[namespace code hide]
}

proc ::tooltip::tooltip {w args} {
    variable tooltip
    variable G
    switch -- $w {
	clear	{
	    if {[llength $args]==0} { set args .* }
	    clear $args
	}
	delay	{
	    if {[llength $args]} {
		if {![string is integer -strict $args] || $args<50} {
		    return -code error "tooltip delay must be an\
			    integer greater than 50 (delay is in millisecs)"
		}
		return [set G(DELAY) $args]
	    } else {
		return $G(DELAY)
	    }
	}
	fade	{
	    if {[llength $args]} {
		set G(fade) [string is true -strict [lindex $args 0]]
	    }
	    return $G(fade)
	}
	off - disable	{
	    set G(enabled) 0
	    hide
	}
	on - enable	{
	    set G(enabled) 1
	}
	default {
	    set i $w
	    if {[llength $args]} {
		set i [uplevel 1 [namespace code "register [list $w] $args"]]
	    }
	    set b $G(TOPLEVEL)
	    if {![winfo exists $b]} {
		variable labelOpts

		toplevel $b -class Tooltip
		if {[tk windowingsystem] eq "aqua"} {
		    ::tk::unsupported::MacWindowStyle style $b help none
		} else {
		    wm overrideredirect $b 1
		}
		catch {wm attributes $b -topmost 1}
		# avoid the blink issue with 1 to <1 alpha on Windows
		catch {wm attributes $b -alpha 0.99}
		wm positionfrom $b program
		wm withdraw $b
		eval [linsert $labelOpts 0 label $b.label]
		pack $b.label -ipadx 1
	    }
	    if {[info exists tooltip($i)]} { return $tooltip($i) }
	}
    }
}

proc ::tooltip::register {w args} {
    variable tooltip
    set key [lindex $args 0]
    while {[string match -* $key]} {
	switch -- $key {
	    -index	{
		if {[catch {$w entrycget 1 -label}]} {
		    return -code error "widget \"$w\" does not seem to be a\
			    menu, which is required for the -index switch"
		}
		set index [lindex $args 1]
		set args [lreplace $args 0 1]
	    }
	    -item - -items {
                if {[winfo class $w] eq "Listbox"} {
                    set items [lindex $args 1]
                } else {
                    set namedItem [lindex $args 1]
                    if {[catch {$w find withtag $namedItem} items]} {
                        return -code error "widget \"$w\" is not a canvas, or\
			    item \"$namedItem\" does not exist in the canvas"
                    }
                }
		set args [lreplace $args 0 1]
	    }
            -tag {
                set tag [lindex $args 1]
                set r [catch {lsearch -exact [$w tag names] $tag} ndx]
                if {$r || $ndx == -1} {
                    return -code error "widget \"$w\" is not a text widget or\
                        \"$tag\" is not a text tag"
                }
                set args [lreplace $args 0 1]
            }
	    -command {
		set command [lindex $args 1]
		set args [lreplace $args 0 1]
	    }
	    default	{
		return -code error "unknown option \"$key\":\
			should be -command, -index, -items or -tag"
	    }
	}
	set key [lindex $args 0]
    }
    if {[llength $args] != 1} {
	return -code error "wrong # args: should be \"tooltip widget\
		?-index index? ?-items item? ?-tag tag? message\""
    }
    if {$key eq ""} {
	clear $w
    } else {
	if {![winfo exists $w]} {
	    return -code error "bad window path name \"$w\""
	}
	set d [dict create message $key]
	if {[info exists command]} {
	    dict set d command $command
	}
	if {[info exists index]} {
	    set tooltip($w,$index) $d
	    return $w,$index
	} elseif {[info exists items]} {
	    foreach item $items {
		set tooltip($w,$item) $d
		if {[winfo class $w] eq "Listbox"} {
		    enableListbox $w $item
		} else {
		    enableCanvas $w $item
		}
	    }
	    # Only need to return the first item for the purposes of
	    # how this is called
	    return $w,[lindex $items 0]
        } elseif {[info exists tag]} {
            set tooltip($w,t_$tag) $d
            enableTag $w $tag
            return $w,$tag
	} else {
	    set tooltip($w) $d
	    bindtags $w [linsert [bindtags $w] end "Tooltip"]
	    return $w
	}
    }
}

proc ::tooltip::clear {{pattern .*}} {
    variable tooltip
    # cache the current widget at pointer
    set ptrw [winfo containing [winfo pointerx .] [winfo pointery .]]
    foreach w [array names tooltip $pattern] {
	unset tooltip($w)
	if {[winfo exists $w]} {
	    set tags [bindtags $w]
	    if {[set i [lsearch -exact $tags "Tooltip"]] != -1} {
		bindtags $w [lreplace $tags $i $i]
	    }
	    ## We don't remove TooltipMenu because there
	    ## might be other indices that use it

	    # Withdraw the tooltip if we clear the current contained item
	    if {$ptrw eq $w} { hide }
	}
    }
}

proc ::tooltip::show {w msg {i {}}} {
    if {![winfo exists $w]} { return }

    # Use string match to allow that the help will be shown when
    # the pointer is in any child of the desired widget
    if {([winfo class $w] ne "Menu")
	&& ![string match $w* [eval [list winfo containing] \
				   [winfo pointerxy $w]]]} {
	return
    }

    variable G

    after cancel $G(FADEID)
    set b $G(TOPLEVEL)
    # Use late-binding msgcat (lazy translation) to support programs
    # that allow on-the-fly l10n changes
    $b.label configure -text [::msgcat::mc $msg] -justify left
    update idletasks
    set screenw [winfo screenwidth $w]
    set screenh [winfo screenheight $w]
    set reqw [winfo reqwidth $b]
    set reqh [winfo reqheight $b]
    # When adjusting for being on the screen boundary, check that we are
    # near the "edge" already, as Tk handles multiple monitors oddly
    if {$i eq "cursor"} {
	set y [expr {[winfo pointery $w]+20}]
	if {($y < $screenh) && ($y+$reqh) > $screenh} {
	    set y [expr {[winfo pointery $w]-$reqh-5}]
	}
    } elseif {$i ne ""} {
	set y [expr {[winfo rooty $w]+[winfo vrooty $w]+[$w yposition $i]+25}]
	if {($y < $screenh) && ($y+$reqh) > $screenh} {
	    # show above if we would be offscreen
	    set y [expr {[winfo rooty $w]+[$w yposition $i]-$reqh-5}]
	}
    } else {
	set y [expr {[winfo rooty $w]+[winfo vrooty $w]+[winfo height $w]+5}]
	if {($y < $screenh) && ($y+$reqh) > $screenh} {
	    # show above if we would be offscreen
	    set y [expr {[winfo rooty $w]-$reqh-5}]
	}
    }
    if {$i eq "cursor"} {
	set x [winfo pointerx $w]
    } else {
	set x [expr {[winfo rootx $w]+[winfo vrootx $w]+
		     ([winfo width $w]-$reqw)/2}]
    }
    # only readjust when we would appear right on the screen edge
    if {$x<0 && ($x+$reqw)>0} {
	set x 0
    } elseif {($x < $screenw) && ($x+$reqw) > $screenw} {
	set x [expr {$screenw-$reqw}]
    }
    if {[tk windowingsystem] eq "aqua"} {
	set focus [focus]
    }
    # avoid the blink issue with 1 to <1 alpha on Windows, watch half-fading
    catch {wm attributes $b -alpha 0.99}
    wm geometry $b +$x+$y
    wm deiconify $b
    raise $b
    if {[tk windowingsystem] eq "aqua" && $focus ne ""} {
	# Aqua's help window steals focus on display
	after idle [list focus -force $focus]
    }
}

proc ::tooltip::_show {w d {i ""}} {
    set message [dict get $d message]
    if {[dict exists $d command]} {
	set message [uplevel #0 [dict get $d command]]
	if {$message eq ""} { return }
    }
    show $w $message $i
}

proc ::tooltip::menuMotion {w} {
    variable G

    if {$G(enabled)} {
	variable tooltip

        # Menu events come from a funny path, map to the real path.
        set m [string map {"#" "."} [winfo name $w]]
	set cur [$w index active]

	# The next two lines (all uses of LAST) are necessary until the
	# <<MenuSelect>> event is properly coded for Unix/(Windows)?
	if {$cur == $G(LAST)} return
	set G(LAST) $cur
	# a little inlining - this is :hide
	after cancel $G(AFTERID)
	catch {wm withdraw $G(TOPLEVEL)}
	if {[info exists tooltip($m,$cur)] || \
		(![catch {$w entrycget $cur -label} cur] && \
		[info exists tooltip($m,$cur)])} {
	    set G(AFTERID) [after $G(DELAY) \
		    [namespace code [list _show $w $tooltip($m,$cur) cursor]]]
	}
    }
}

proc ::tooltip::hide {{fadeOk 0}} {
    variable G

    after cancel $G(AFTERID)
    after cancel $G(FADEID)
    if {$fadeOk && $G(fade)} {
	fade $G(TOPLEVEL) $G(FADESTEP)
    } else {
	catch {wm withdraw $G(TOPLEVEL)}
    }
}

proc ::tooltip::fade {w step} {
    if {[catch {wm attributes $w -alpha} alpha] || $alpha <= 0.0} {
        catch { wm withdraw $w }
        catch { wm attributes $w -alpha 0.99 }
    } else {
	variable G
        wm attributes $w -alpha [expr {$alpha-$step}]
        set G(FADEID) [after 50 [namespace code [list fade $w $step]]]
    }
}

proc ::tooltip::wname {{w {}}} {
    variable G
    if {[llength [info level 0]] > 1} {
	# $w specified
	if {$w ne $G(TOPLEVEL)} {
	    hide
	    destroy $G(TOPLEVEL)
	    set G(TOPLEVEL) $w
	}
    }
    return $G(TOPLEVEL)
}

proc ::tooltip::listitemTip {w x y} {
    variable tooltip
    variable G

    set G(LAST) -1
    set item [$w index @$x,$y]
    if {$G(enabled) && [info exists tooltip($w,$item)]} {
	set G(AFTERID) [after $G(DELAY) \
		[namespace code [list _show $w $tooltip($w,$item) cursor]]]
    }
}

# Handle the lack of <Enter>/<Leave> between listbox items using <Motion>
proc ::tooltip::listitemMotion {w x y} {
    variable tooltip
    variable G
    if {$G(enabled)} {
        set item [$w index @$x,$y]
        if {$item ne $G(LAST)} {
            set G(LAST) $item
            after cancel $G(AFTERID)
            catch {wm withdraw $G(TOPLEVEL)}
            if {[info exists tooltip($w,$item)]} {
                set G(AFTERID) [after $G(DELAY) \
                   [namespace code [list _show $w $tooltip($w,$item) cursor]]]
            }
        }
    }
}

# Initialize tooltip events for Listbox widgets
proc ::tooltip::enableListbox {w args} {
    if {[string match *listitemTip* [bind $w <Enter>]]} { return }
    bind $w <Enter> +[namespace code [list listitemTip %W %x %y]]
    bind $w <Motion> +[namespace code [list listitemMotion %W %x %y]]
    bind $w <Leave> +[namespace code [list hide 1]] ; # fade ok
    bind $w <Any-KeyPress> +[namespace code hide]
    bind $w <Any-Button> +[namespace code hide]
}

proc ::tooltip::itemTip {w args} {
    variable tooltip
    variable G

    set G(LAST) -1
    set item [$w find withtag current]
    if {$G(enabled) && [info exists tooltip($w,$item)]} {
	set G(AFTERID) [after $G(DELAY) \
		[namespace code [list _show $w $tooltip($w,$item) cursor]]]
    }
}

proc ::tooltip::enableCanvas {w args} {
    if {[string match *itemTip* [$w bind all <Enter>]]} { return }
    $w bind all <Enter> +[namespace code [list itemTip $w]]
    $w bind all <Leave>	+[namespace code [list hide 1]] ; # fade ok
    $w bind all <Any-KeyPress> +[namespace code hide]
    $w bind all <Any-Button> +[namespace code hide]
}

proc ::tooltip::tagTip {w tag} {
    variable tooltip
    variable G
    set G(LAST) -1
    if {$G(enabled) && [info exists tooltip($w,t_$tag)]} {
        if {[info exists G(AFTERID)]} { after cancel $G(AFTERID) }
        set G(AFTERID) [after $G(DELAY) \
            [namespace code [list _show $w $tooltip($w,t_$tag) cursor]]]
    }
}

proc ::tooltip::enableTag {w tag} {
    if {[string match *tagTip* [$w tag bind $tag]]} { return }
    $w tag bind $tag <Enter> +[namespace code [list tagTip $w $tag]]
    $w tag bind $tag <Leave> +[namespace code [list hide 1]] ; # fade ok
    $w tag bind $tag <Any-KeyPress> +[namespace code hide]
    $w tag bind $tag <Any-Button> +[namespace code hide]
}

package provide tooltip 1.4.4
# Copyright 1999-2006,2008-2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

if {[info exists ::env(BK_DEBUG_GUI)]} {
	proc InCommand {} {
		uplevel {puts "[string repeat { } [expr {[info level] - 1}]][info level 0]"}
	}

	proc newproc {name args body} {
		set body "InCommand\n$body"
		realproc $name $args $body
	}

	rename proc realproc
	rename newproc proc
}

if {![info exists ::env(BK_GUI_LEVEL)]
    || ![string is integer -strict $::env(BK_GUI_LEVEL)]} {
	set ::env(BK_GUI_LEVEL) 0
}
incr ::env(BK_GUI_LEVEL)

proc bk_toolname {} {
	if {[info exists ::tool_name]} {
		return $::tool_name
	}
	return [file tail [info script]]
}

proc bk_toplevel {} {
	if {[bk_toolname] eq "citool"} { return ".citool" }
	return "."
}

proc bk_initTheme {} \
{
	switch -- [tk windowingsystem] {
		"aqua" {
			set bg "systemSheetBackground"
		}

		"x11" {
			ttk::setTheme bk
		}
	}

	set bg [ttk::style lookup . -background]

	. configure -background $bg
	option add *background	$bg

	option add *Frame.background	$bg
	option add *Label.background	$bg
	option add *Toplevel.background	$bg
	option add *Listbox.background	#FFFFFF
	option add *Entry.background	#FFFFFF
	option add *Entry.borderWidth	1
	option add *Text.background	#FFFFFF
	## Work around a Tk bug in OS X.
	if {[tk windowingsystem] == "aqua"} {
		option add *Menu.background systemMenu
	}

	## Make the ReadOnly tag
	foreach event [bind Text] {
		set script [bind Text $event]
		if {[regexp -nocase {text(paste|insert|transpose)} $script]
		    || [regexp -nocase {%W (insert|delete|edit)} $script]} {
			continue
		}
		set script [string map {tk_textCut tk_textCopy} $script]
		bind ReadonlyText $event $script
	}
	bind ReadonlyText <Up>	    "%W yview scroll -1 unit; break"
	bind ReadonlyText <Down>    "%W yview scroll  1 unit; break"
	bind ReadonlyText <Left>    "%W xview scroll -1 unit; break"
	bind ReadonlyText <Right>   "%W xview scroll  1 unit; break"
	bind ReadonlyText <Prior>   "%W yview scroll -1 page; break"
	bind ReadonlyText <Next>    "%W yview scroll  1 page; break"
	bind ReadonlyText <Home>    "%W yview moveto 0; break"
	bind ReadonlyText <End>	    "%W yview moveto 1; break"
}

proc bk_init {} {
	set tool [bk_toolname]

	bk_initPlatform

	bk_initTheme

	## Include our tool name and Toplevel tags for .
	bindtags . [list $tool . Toplevel all]

	## Remove default Tk mouse wheel bindings.
	foreach event {MouseWheel 4 5} {
		foreach mod {"" Shift- Control- Command- Alt- Option-} {
			catch {bind Text <$mod$event> ""}
			catch {bind Listbox <$mod$event> ""}
		}
	}

	## Mouse wheel bindings
	if {[tk windowingsystem] eq "x11"} {
		bind all <4> {scrollMouseWheel %W y %X %Y -1}
		bind all <5> {scrollMouseWheel %W y %X %Y  1}
		bind all <Shift-4> {scrollMouseWheel %W x %X %Y -1}
		bind all <Shift-5> {scrollMouseWheel %W x %X %Y  1}

		bind wheel <4> {scrollMouseWheel %W y %X %Y -1}
		bind wheel <5> {scrollMouseWheel %W y %X %Y  1}
		bind wheel <Shift-4> {scrollMouseWheel %W x %X %Y -1}
		bind wheel <Shift-5> {scrollMouseWheel %W x %X %Y  1}
	} else {
		bind all <MouseWheel> {scrollMouseWheel %W y %X %Y %D}
		bind all <Shift-MouseWheel> {scrollMouseWheel %W x %X %Y %D}

		bind wheel <MouseWheel> {scrollMouseWheel %W y %X %Y %D}
		bind wheel <Shift-MouseWheel> {scrollMouseWheel %W x %X %Y %D}
	}

	if {[tk windowingsystem] eq "aqua"} {
		event add <<Redo>> <Command-Shift-z> <Command-Shift-Z>
	}

	bind Entry  <KP_Enter> {event generate %W <Return>}
	bind TEntry <KP_Enter> {event generate %W <Return>}
}

# 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 cd2product {{path ""}} {
	set cmd [list exec bk root]
	if {$path ne ""} { lappend cmd $path }
	if {[catch { cd [{*}$cmd] } err]} {
		puts "Could not change directory to product root."
		exit 1
	}
}

proc resolveSymlink {filename} {
	catch {
		set original_path [file dirname $filename]
		set link_path [file readlink $filename]
		set filename [file join $original_path $link_path]
		# once we upgrade to tcl 8.4 we should also call 
		# [file normalize]...
	}
	return $filename
}

proc displayMessage {msg {exit {}}} \
{
	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 \
	    -parent [bk_toplevel]
	if {$exit == 1} {
		exit 1
	} else {
		return
	}
}

proc message {message args} \
{
	if {[dict exists $args -exit]} {
		set exit [dict get $args -exit]
		dict unset args -exit
	}

	if {![dict exists $args -parent]} {
		dict set args -parent [bk_toplevel]
	}

	set forceGui 0
	if {[dict exists $args -gui]} {
	    set forceGui 1
	    dict unset args -gui
	}

	if {!$forceGui && ([info exists ::env(BK_REGRESSION)]
	    || ($::env(BK_GUI_LEVEL) == 1
		&& $::tcl_platform(platform) ne "windows"))} {
		if {[info exists ::env(BK_REGRESSION)]
		    && $::env(BK_GUI_LEVEL) > 1} {
			append message " (LEVEL: $::env(BK_GUI_LEVEL))"
		}
		puts stderr $message
	} else {
		tk_messageBox {*}$args -message $message
	}

	if {[info exists exit]} {
		exit $exit
	}
}

# usage: centerWindow pathName ?width height?
#
# If width and height are supplied the window will be set to
# that size and that size will be used to compute the location
# of the window. Otherwise the requested width and height of the
# window will be used.
proc centerWindow {w args} \
{

	set w [winfo toplevel $w]

	if {[llength $args] > 0} {
		set width [lindex $args 0]
		set height [lindex $args 1]
	} else {
		set width [winfo reqwidth $w]
		set height [winfo reqheight $w]
	}
	set x [expr {round(([winfo vrootwidth $w] - $width) /2)}]
	set y [expr {round(([winfo vrootheight $w] - $height) /2)}]

	wm geometry $w +${x}+${y}
}

# this proc attempts to center a given line number in a text widget;
# similar to the widget's "see" option but with the requested line
# always centered, if possible. The text widget "see" command only
# attempts to center a line if it is "far out of view", so we first
# try to scroll the requested line as far away as possible, then
# scroll it back. Kludgy, but it seems to work.
proc centerTextLine {w line} \
{
	set midline "[expr {int([$w index end-1c] / 2)}].0"
	if {$line > $midline} {
		$w see 1.0
	} else {
		$w see end
	}
	update idletasks
	$w see $line
}

# 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 tmpfile {name} \
{
	global	tmp_dir tmp_files tmp_filecount

	set prefix [file join $tmp_dir "bk_${name}_[pid]"]
	set filename "${prefix}_[incr tmp_filecount].tmp"
	while {[file exists $filename]} {
		set filename "${prefix}_[incr tmp_filecount].tmp"
	}
	lappend tmp_files $filename
	return $filename
}

## Setup a trace to cleanup any temporary files as we exit.
proc cleanupTmpfiles {args} \
{
	catch {
		global	tmp_files
		foreach file $tmp_files {
			file delete -force $file
		}
	}
}
trace add exec exit enter cleanupTmpfiles

proc loadState {appname} \
{
	catch {::appState load $appname ::State}
}

proc saveState {appname} \
{
	catch {::appState save $appname ::State}
}

proc getScreenSize {{w .}} \
{
	return [winfo vrootwidth $w]x[winfo vrootheight $w]
}

proc trackGeometry {w1 w2 width height} \
{	
	global	gc app

	# The event was caused by a different widget
	if {$w1 ne $w2} {return}

	# We don't want to save the geometry if the user maximized
	# the window, so only save if it's a 'normal' resize operation.
	# XXX: Only works on MS Windows
	if {[wm state $w1] eq "normal"} {
		set min $gc($app.minsize)
		set res [getScreenSize $w1]
		if {$width < $min || $height < $min} {
			debugGeom "Geometry ${width}x${height} too small"
			return
		}
		# We can't get width/height from wm geometry because if the 
		# app is gridded, we'll get grid units instead of pixels.
		# The parameters (%w %h) however, seem to 
		# be correct on all platforms.
		foreach {- - ox x oy y} [goodGeometry [wm geometry $w1]] {break}
		set ::State(geometry@$res) "${width}x${height}${ox}${x}${oy}${y}"
		debugGeom "Remembering $::State(geometry@$res)"
	}
}

# See if a geometry string is good. Returns a list with 
# [width, height, ox, x, oy , y] where ox and oy are the sign
# of the geometry string (+|-)
proc goodGeometry {geometry} \
{
	if {[regexp \
    	    {(([0-9]+)[xX]([0-9]+))?(([\+\-])([\+\-]?[0-9]+)([\+\-])([\+\-]?[0-9]+))?} \
	    $geometry - - width height - ox x oy y]} {
		return [list $width $height $ox $x $oy $y]
	}
	return ""
}

proc debugGeom {args} \
{
	global env

	if {[info exists env(BK_DEBUG_GEOMETRY)]} {
		puts stderr [join $args " "]
	}
}

proc restoreGeometry {app {w .}} \
{
	global State gc env

	debugGeom "start"
	# track geometry changes 
	bindtags $w [concat "geometry" [bindtags $w]]
	bind geometry <Configure> [list trackGeometry $w %W %w %h]

	set rwidth [winfo vrootwidth $w]
	set rheight [winfo vrootheight $w]
	set res ${rwidth}x${rheight}
	debugGeom "res $res"

	# get geometry from the following priority list (most to least)
	# 1. -geometry on the command line (which means ::geometry)
	# 2. _BK_GEOM environment variable
	# 3. State(geometry@res) (see loadState && saveState)
	# 4. gc(app.geometry) (see config.tcl)
	# 5. App request (whatever Tk wants)
	# We stop at the first usable geometry...

	if {[info exists ::geometry] && 
	    ([set g [goodGeometry $::geometry]] ne "")} {
		debugGeom "Took ::geometry"
	} elseif {[info exists env(_BK_GEOM)] &&
	    ([set g [goodGeometry $env(_BK_GEOM)]] ne "")} {
		debugGeom "Took _BK_GEOM"
	} elseif {[info exists State(geometry@$res)] &&
	    ([set g [goodGeometry $State(geometry@$res)]] ne "")} {
		debugGeom "Took State"
	} elseif {[info exists gc($app.geometry)] &&
	    ([set g [goodGeometry $gc($app.geometry)]] ne "")} {
		debugGeom "Took app.geometry"
	}
	
	# now get the variables
	if {[info exists g]} {
		foreach {width height ox x oy y} $g {break}
		debugGeom "config: $width $height $ox $x $oy $y"
	} else {
		set width ""
		set x ""
	}
	
	if {$width eq ""} {
		# We need to call update to force the recalculation of
		# geometry. We're assuming the state of the widget is
		# withdrawn so this won't cause a screen update.
		update
		set width [winfo reqwidth $w]
		set height [winfo reqheight $w]
	}
	
	if {$x eq ""} {
		foreach {- - ox x oy y} [goodGeometry [wm geometry $w]] {break}
	}
	debugGeom "using: $width $height $ox $x $oy $y"
	
	# The geometry rules are different for each platform.
	# E.g. in Mac OS X negative positions for the geometry DO NOT
	# correspond to the lower right corner of the app, it's ALWAYS
	# the top left corner. (This will change with Tk-8.4.12 XXX)
	# Thus, we ALWAYS specify the geometry as top left corner for
	# BOTH the app and the screen. The math may be harder, but it'll
	# be right.

	# Usable space
	set ux $gc(padLeft)
	set uy $gc(padTop)
	set uwidth [expr {$rwidth - $gc(padLeft) - $gc(padRight)}]
	set uheight [expr {$rheight - $gc(padTop) 
	    - $gc(padBottom) - $gc(titlebarHeight)}]
	debugGeom "ux: $ux uy: $uy uwidth: $uwidth uheight: $uheight"
	debugGeom "padLeft $gc(padLeft) padRight $gc(padRight)"
	debugGeom "padTop $gc(padTop) padBottom $gc(padBottom)"
	debugGeom "titlebarHeight $gc(titlebarHeight)"

	# Normalize the app's position. I.e. (x, y) is top left corner of app
	if {$ox eq "-"} {set x [expr {$rwidth - $x - $width}]}
	if {$oy eq "-"} {set y [expr {$rheight - $y - $height}]}

	if {![info exists env(BK_GUI_OFFSCREEN)]} {
		# make sure 100% of the GUI is visible
		debugGeom "Size start $width $height"
		set width [expr {($width > $uwidth)?$uwidth:$width}]
		set height [expr {($height > $uheight)?$uheight:$height}]
		debugGeom "Size end $width $height"

		debugGeom "Pos start $x $y"
		if {$x < $ux} {set x $ux}
		if {$y < $uy} {set y $uy}
		if {($x + $width) > ($ux + $uwidth)} {
			debugGeom "1a $ox $x $oy $y"
			set x [expr {$ux + $uwidth - $width}]
			debugGeom "1b $ox $x $oy $y"
		}
		if {($y + $height) > ($uy + $uheight)} {
			debugGeom "2a $ox $x $oy $y"
			set y [expr {$uy + $uheight - $height}]
			debugGeom "2b $ox $x $oy $y"
		}
		debugGeom "Pos end $x $y"
	} else {
		debugGeom "Pos start offscreen $x $y"
		# make sure at least some part of the window is visible
		# i.e. we don't care about size, only position
		# if the app is offscreen, we pull it so that 1/10th of it
		# is visible
		if {$x > ($ux + $uwidth)} {
			set x [expr {$ux + $uwidth - int($uwidth/10)}]
		} elseif {($x + $width) < $ux} {
			set x $ux
		}
		if {$y > ($uy + $uheight)} {
			set y [expr {$uy + $uheight - int($uheight/10)}]
		} elseif {($y + $height) < $uy} {
			set y $uy
		}
		debugGeom "Pos end offscreen $x $y"
	}


	# Since we are setting the size of the window we must turn
	# geometry propagation off
	catch {grid propagate $w 0}
	catch {pack propagate $w 0}

	debugGeom "${width}x${height}"
	# Don't use [wm geometry] for width and height because it 
	# treats the arguments as grid units if the widget is in grid mode.
	$w configure -width $width -height $height

	debugGeom "+$x +$y"
	wm geometry $w +${x}+${y}
}

# this removes hardcoded newlines from paragraphs so that the paragraphs
# will wrap when placed in a widget that wraps (such as the description
# of a step)
proc wrap {text} \
{
	if {$::tcl_version >= 8.2} {
		set text [string map [list \n\n \001 \n { }] $text]
		set text [string map [list \001 \n\n] $text]
	} else {
		regsub -all "\n\n" $text \001 text
		regsub -all "\n" $text { } text
		regsub -all "\001" $text \n\n text
	}
	return $text
}

# get a message from the bkmsg.doc message catalog
proc getmsg {key args} \
{
	# do we want to return something like "lookup failed for xxx"
	# if the lookup fails? What we really need is something more
	# like real message catalogs, where I can supply messages that
	# have defaults.
	set data ""
	set cmd [list bk getmsg $key]
	if {[llength $args] > 0} {
		lappend cmd [lindex $args 0]
	}
	set err [catch {set data [eval exec $cmd]}]
	return $data
}

# usage: bgExec ?options? command ?arg? ?arg ..?
#
# this command exec's a program, waits for it to finish, and returns
# the exit code of the exec'd program.  Unlike a normal "exec" call, 
# while the pipe is running the event loop is active, allowing the 
# calling GUI to be refreshed as necessary. However, this proc will 
# not allow the user to interact with the calling program until it 
# returns, by doing a grab on an invisible window.
#
# Upon completion, stdout from the command is available in the global
# variable bgExec(stdout), and stderr is in bgExec(stderr)
#
#
# Options:
# 
# -output proc
#
#    proc is the name of a proc to be called whenever output is
#    generated by the command. The proc will be called with two
#    arguments: the file descriptor (useful as a unique identifier) and
#    the data that was read in.
#
# example: 
#
#    text .t ; pack .t
#    proc showOutput {f string} {
#        .t insert end $string
#        return $string
#    }
#    set exitStatus [bgExec -output showOutput ls]
#
# Side effects:
#
# while running, this creates a temporary window named .__grab__<fd>,
# where <fd> is the file description of the open pipe

namespace eval ::bgExec {}
interp alias {} ::bgExec {} ::bgExec::bgExec

proc ::bgExec::bgExec {args} \
{
	global bgExec errorCode

	set outhandler ""
	while {[llength $args] > 1} {
		set arg [lindex $args 0]
		switch -exact -- $arg {
			-output {
				set outhandler [lindex $args 1]
				set args [lrange $args 2 end]
			}
			--	{
				set args [lrange $args 1 end]
				break
			}
			default	{break}
		}
	}

	set stderrFile [tmpfile "bgexec-stderr"]
	set run_fd [open |[list {*}$args 2> $stderrFile] "r"]
	fconfigure $run_fd -blocking false
	fileevent $run_fd readable [namespace code [list readFile $run_fd]]

	set bgExec(handler) $outhandler
	set bgExec(stdout) ""
	set bgExec(stderr) ""
	set bgExec(status) 0

	# Create a small, invisible window, and do a grab on it
	# so the user can't interact with the main program.
	set grabWin .__grab__$run_fd
	frame $grabWin -width 1 -height 1 -background {} -borderwidth 0
	place $grabWin -relx 1.0 -x -2 -rely 1.0 -y -2
	after idle "if {\[winfo exists $grabWin]} {grab $grabWin}"

	# This variable is set by the code that gets run via the 
	# fileevent handler when we get EOF on the pipe.
	vwait bgExec(status)

	catch {destroy $grabWin}

	# The pipe must be reconfigured to blocking mode before
	# closing, or close won't wait for the process to end. If
	# close doesn't wait, we can't get the exit status.
	fconfigure $run_fd -blocking true
	set ::errorCode [list NONE]
	catch {close $run_fd}
	if {[info exists ::errorCode] && 
	    [lindex $::errorCode 0] == "CHILDSTATUS"} {
		set exitCode [lindex $::errorCode 2]
	} else {
		set exitCode 0
	}

	if {[file exists $stderrFile]} {
		set f [open $stderrFile r]
		set bgExec(stderr) [read $f]
		close $f
		file delete $stderrFile
	}

	unset bgExec(handler)
	unset bgExec(status)

	return $exitCode
}

proc ::bgExec::handleOutput {f string} \
{
	global bgExec

	if {[info exists bgExec(handler)] && $bgExec(handler) != ""} {
		set tmp [$bgExec(handler) $f $string]
		append bgExec(stdout) $tmp
	} else {
		append bgExec(stdout) $string
	}
}

proc ::bgExec::readFile {f} \
{
	global bgExec

	# The channel is readable; try to read it.
	set status [catch { gets $f line } result]

	if { $status != 0 } {
		# Error on the channel
		set bgExec(status) $status

	} elseif { $result >= 0 } {
		# Successfully read the channel
		handleOutput $f "$line\n"

	} elseif { [eof $f] } {
		# End of file on the channel
		set bgExec(status) 1

	} elseif { [fblocked $f] } {
		# Read blocked.  Just return

	} else {
		# Something else; should never happen.
		set bgExec(status) 2
	}
}

proc popupMessage {args} \
{
	if {[llength $args] == 1} {
		set option ""
		set message [lindex $args 0]
	} else {
		set option [lindex $args 0]
		set message [lindex $args 1]
	}

	# export BK_MSG_GEOM so the popup will show in the right
	# place...
	if {[winfo viewable .] || [winfo viewable .citool]} {
		set x [expr {[winfo rootx .] + 40}]
		set y [expr {[winfo rooty .] + 40}]
		set ::env(BK_MSG_GEOM) "+${x}+${y}"
	}

	set tmp [tmpfile msg]
	set fp [open $tmp w]
	puts $fp $message
	close $fp

	# hopefully someday we'll turn the msgtool code into a library
	# so we don't have to exec. For now, though, exec works just fine.
	if {[info exists ::env(BK_REGRESSION)]} {
		# we are running in test mode; spew to stderr
		puts stderr $message
	} else {
		exec bk msgtool {*}$option -F $tmp
	}
}

# License Functions

proc checkLicense {license licsign1 licsign2 licsign3} \
{
	global dev_null

	# bk _eula -v has the side effect of popping up a messagebox
	# warning the user if the license is invalid. 
	set f [open "|bk _eula -v > $dev_null" w]
	puts $f "
	    license: $license
	    licsign1: $licsign1
	    licsign2: $licsign2
	    licsign3: $licsign3
	"

	set ::errorCode NONE
	catch {close $f}
		      
	if {($::errorCode == "NONE") || 
	    ([lindex $::errorCode 0] == "CHILDSTATUS" &&
	     [lindex $::errorCode 2] == 0)} {
		return 1
	}
	return 0
}

# Side Effect: the license data is put in the environment variable BK_CONFIG
proc getEulaText {license licsign1 licsign2 licsign3 text} \
{
	global env
	upvar $text txt

	# need to override any config currently in effect...
	set BK_CONFIG "logging:none!;"
	append BK_CONFIG "license:$license!;"
	append BK_CONFIG "licsign1:$licsign1!;"
	append BK_CONFIG "licsign2:$licsign2!;"
	append BK_CONFIG "licsign3:$licsign3!;"
	append BK_CONFIG "single_user:!;single_host:!;"
	set env(BK_CONFIG) $BK_CONFIG
	set r [catch {exec bk _eula -u} txt]
	if {$r} {set txt ""}
	return $r
}

proc normalizePath {path} \
{
	return [file join {*}[file split $path]]
}

# run 'script' for each line in the text widget
# binding 'var' to the contents of the line
# e.g.
#
# EACH_LINE .t myline {
#	puts $myline
# }
#
# would dump the contents of the text widget on stdout
# Note that 'myline' will still exist after the script
# is done. Also, if 'myline' existed before EACH_LINE
# is called, it will be stomped on.
proc EACH_LINE {widget var script} {
	upvar 1 $var line
	set lno 1.0
	while {[$widget compare $lno < [$widget index end]]} {
		set line [$widget get $lno "$lno lineend"]
		set lno [$widget index "$lno + 1 lines"]
		# careful, the script must be run at the end
		# because of 'continue' and 'break'
		uplevel 1 $script
	}
}

# Aqua stuff

proc AboutAqua {} \
{
	if {[winfo exists .aboutaqua]} {return}
	set version [exec bk version]
	toplevel .aboutaqua
	wm title .aboutaqua ""
	frame .aboutaqua.f
	::tk::unsupported::MacWindowStyle style .aboutaqua document {closeBox}
	label .aboutaqua.f.title \
	    -text "The BitKeeper Configuration Management System" \
	    -font {Helvetica 14 bold} \
	    -justify center
	label .aboutaqua.f.v \
	    -text $version \
	    -font {Helvetica 12 normal} \
	    -justify left
	label .aboutaqua.f.copyright \
	    -text "Copyright 2015 BitKeeper Inc." \
	    -font {Helvetica 11 normal} \
	    -justify center
	grid .aboutaqua.f.title -pady 2
	grid .aboutaqua.f.v -pady 2
	grid .aboutaqua.f.copyright -pady 2 -sticky we
	grid .aboutaqua.f  -padx 20 -pady 20 -sticky nswe
}

proc AquaMenus {} \
{
	menu .mb
	. configure -menu .mb
	menu .mb.apple -tearoff 0
	.mb.apple add command -label "About BitKeeper" -command AboutAqua
	.mb add cascade -menu .mb.apple
	menu .mb.help -tearoff 0
	.mb add cascade -menu .mb.help
	.mb.help add command \
	    -label "BitKeeper Help" -command {exec bk helptool &}
}

# Mac OS X needs a _real_ menubar 
if {[tk windowingsystem] eq "aqua"} {
	AquaMenus
}

proc GetTerminal {} {
	set term xterm
	if {[info exists ::env(TERMINAL)]} { set term $::env(TERMINAL) }
	if {[auto_execok $term] eq ""} { return }
	return $term
}

proc isComponent {path} {
	catch {exec bk repotype [file dirname $path]} res
	return [string equal $res "component"]
}

proc isChangeSetFile {path} {
	if {[file tail $path] eq "ChangeSet"
	    && [file isdir [file join [file dirname $path] BitKeeper etc]]} {
		return 1
	}
	return 0
}

proc sccsFile {type file} {
	return [file join [file dirname $file] SCCS $type.[file tail $file]]
}

proc sccsFileExists {type file} {
	set file [sccsFile $type $file]
	if {[catch {exec bk _test -f $file}]} { return 0 }
	return 1
}

proc inComponent {} {
    catch {exec bk repotype} res
    return [string equal $res "component"]
}

proc inRESYNC {} {
    set dir [file tail [exec bk root -S]]
    return [string equal $dir "RESYNC"]
}

## Attach any number of widgets (usually 2 diff widgets) to a single scrollbar.
## We remember the list of widgets for a given scrollbar and then make sure
## those widgets stay in sync when one of them is scrolled.
proc attachScrollbar {sb args} \
{
	global	gc

	set xy x
	if {[$sb cget -orient]  eq "vertical"} { set xy y }

	## Configure the scrollbar to call our custom scrolling function.
	$sb configure -command [list scrollWidgets $sb ${xy}view]

	## Keep track of which widgets are attached to this scrollbar
	## and then tell each widget what its new X/Y scrollcommand is.
	dict set gc(scrollbar.widgets) $sb $args
	foreach w $args {
		$w configure -${xy}scrollcommand [list setScrollbar $sb $w]
	}
}

## This gets called when you actually manipulate the scrollbar itself.  We
## just take the scrollbar, grab the list of widgets associated with it
## and scroll them all with the command given.
proc scrollWidgets {sb args} \
{
	global	gc

	## Just scroll everyone attached to the scrollbar with the same
	## command.
	foreach widg [dict get $gc(scrollbar.widgets) $sb] {
		$widg {*}$args
	}
}

## This gets called by an attached widget anytime something in the widget
## view has changed and it wants to update the scrollbar to tell it where
## it should be.  This happens on things like mousewheel movement and drag
## scrolling.
##
## Since the widget being controlled will already be moving by the proper
## amount, we just take any other widget in our list and make it match the
## exact coordinates that the primary widget is already at.
proc setScrollbar {sb w first last} \
{
	global	gc

	## Tell the scrollbar what to look like.
	$sb set $first $last
	if {![dict exists $gc(scrollbar.widgets) $sb]} { return }

	## Grab the current coordinates for the primary widget being scrolled.
	set x [lindex [$w xview] 0]
	set y [lindex [$w yview] 0]

	## Move all widgets that aren't the primary widget to the same point.
	foreach widg [dict get $gc(scrollbar.widgets) $sb] {
		if {$widg eq $w} { continue }
		$widg xview moveto $x
		$widg yview moveto $y
	}
}

proc scrollMouseWheel {w dir x y delta} \
{
	set widg [winfo containing $x $y]
	if {$widg eq ""} { set widg $w }

	switch -- [tk windowingsystem] {
	    "aqua"  { set delta [expr {-$delta}] }
	    "x11"   { set delta [expr {$delta * 3}] }
	    "win32" { set delta [expr {($delta / 120) * -3}] }
	}

	## If we fail to scroll the widget the mouse is
	## over for some reason, just scroll the widget
	## with focus.
	if {[catch {$widg ${dir}view scroll $delta units}]} {
		catch {$w ${dir}view scroll $delta units}
	}
}

proc isBinary { filename } \
{
    global	gc

    set fd [open $filename rb]
    set x  [read $fd $gc(ci.display_bytes)]
    catch {close $fd}
    return [regexp {[\x00-\x08\x0b\x0e-\x1f]} $x]
}

proc display_text_sizes {{onOrOff ""}} {
	if {$onOrOff ne ""} { set ::display_text_sizes $onOrOff }
	return $::display_text_sizes
}

set ::display_text_sizes on

proc displayTextSize {text w h} \
{
	if {!$::display_text_sizes} { return }
	if {![info exists ::textWidgets($text,w)]} { return }

	set oldW $::textWidgets($text,w)
	set oldH $::textWidgets($text,h)

	## Check to see if size has changed.
	if {abs($w - $oldW) <= 2 && abs($h - $oldH) <= 2} { return }

	set ::textWidgets($text,w) $w
	set ::textWidgets($text,h) $h

	## Don't do anything on the initial draw.
	if {$oldW == 0 && $oldH == 0} { return }

	if {[info exists ::textSize($text)]} {
		after cancel $::textSize($text)
	}

	if {$w <= 1 && $h <= 1} { return }

	set font  [$text cget -font]
	set fontW [font measure $font 0]
	set fontH [dict get [font metrics $font] -linespace]

	set cwidth  [expr {$w / $fontW}]
	set cheight [expr {$h / $fontH}]

	if {$cwidth <= 1 || $cheight <= 1} { return }

	set label $text.__size
	place $label -x 0 -y 0
	$label configure -text "${cwidth}x${cheight}"

	set ::textSize($text) [after 1000 [list hideTextDisplaySize $text]]
}

proc hideTextDisplaySize {w} \
{
	place forget $w.__size
}

## Trace the text command to grab new text widgets as they are
## created and add bind their <Configure> event to show text
## size when they are changed.
proc traceTextWidget {cmd code widget event} \
{
	set ::textWidgets($widget,w) 0
	set ::textWidgets($widget,h) 0

	## Bind the <Map> event so that the first time the text widget
	## is drawn, we configure our display text size popups.  Note
	## that the reason we do this is so that we don't display the
	## text size popups on the initial draw of the widget but only
	## after they've been resized at some point by the user.
	bind $widget <Map> {
	    bind %W <Map> ""
	    bind %W <Configure> "displayTextSize %%W %%w %%h"
	}
	label $widget.__size -relief solid -borderwidth 1 -background #FEFFE6
}
trace add exec text leave traceTextWidget


## This is actually overriding a core Tk proc that is called whenever
## the X11 paste selection code is called.  Where that code moves the
## insertion cursor before pasting, we just want to paste where the
## insert cursor already is.
proc ::tk::TextPasteSelection {w x y} \
{
    if {![catch {::tk::GetSelection $w PRIMARY} sel]} {
	    set oldSeparator [$w cget -autoseparators]
	    if {$oldSeparator} {
		    $w configure -autoseparators 0
		    $w edit separator
	    }
	    $w insert insert $sel
	    if {$oldSeparator} {
		    $w edit separator
		    $w configure -autoseparators 1
	    }
    }
    if {[$w cget -state] eq "normal"} {
	    focus $w
    }
}

## Override another Tk core proc.  Tk seems to think that on X11 we should
## not delete any current selection when pasting.  The more modern behavior
## is to always replace any current selection with the clipboard contents.
proc ::tk_textPaste {w} \
{
	global tcl_platform
	if {![catch {::tk::GetSelection $w CLIPBOARD} sel]} {
		set oldSeparator [$w cget -autoseparators]
		if {$oldSeparator} {
			$w configure -autoseparators 0
			$w edit separator
		}
		catch { $w delete sel.first sel.last }
		$w insert insert $sel
		if {$oldSeparator} {
			$w edit separator
			$w configure -autoseparators 1
		}
	}
}

#lang L
typedef struct hunk {
	int	li, ri;		/* left/right indices */
	int	ll, rl;		/* left/right lengths */
} hunk;


/*
 *    chg   index
 *    0
 *    0
 *    1 <--- a
 *    1
 *    1
 *    0 <--- b
 *    0
 *    0 <--- c
 *    1
 *    1
 *    1 <--- d
 *    0
 */
void
shrink_gaps(string S, int &chg[])
{
	int	i, n;
	int	a, b, c, d;
	int	a1, b1, c1, d1;

	n = length(chg);
	i = 0;
	/* Find find non-zero line for 'a' */
	while ((i < n) && (chg[i] == 0)) i++;
	if (i == n) return;
	a = i;
	/* Find next zero line for 'b' */
	while ((i < n) && (chg[i] == 1)) i++;
	if (i == n) return;
	b = i;

	while (1) {
		/* The line before the next 1 is 'c' */
		while ((i < n) && (chg[i] == 0)) i++;
		if (i == n) return;
		c = i - 1;

		/* The last '1' is 'd' */
		while ((i < n) && (chg[i] == 1)) i++;
		/* hitting the end here is OK */
		d = i - 1;

	again:
		/* try to close gap between b and c */
		a1 = a; b1 = b; c1 = c; d1 = d;
		while ((b1 <= c1) && (S[a1] == S[b1])) {
			a1++;
			b1++;
		}
		while ((b1 <= c1) && (S[c1] == S[d1])) {
			c1--;
			d1--;
		}

		if (b1 > c1) {
			/* Bingo! commit it */
			while (a < b) chg[a++] = 0;  /* erase old block */
			a = a1;
			while (a < b1) chg[a++] = 1;  /* write new block */
			a = a1;
			b = b1;
			while (d > c) chg[d--] = 0;
			d = d1;
			while (d > c1) chg[d--] = 1;
			c = c1;
			d = d1;

			/*
			 * now search back for previous block and start over.
			 * The last gap "might" be closable now.
			 */
			--a;
			c = a;
			while ((a > 0) && (chg[a] == 0)) --a;
			if (chg[a] == 1) {
				/* found a previous block */
				b = a+1;
				while ((a > 0) && (chg[a] == 1)) --a;
				if (chg[a] == 0) ++a;
				/*
				 * a,b nows points at the previous block
				 * and c,d points at the newly merged block
				 */
				goto again;
			} else {
				/*
				 * We were already in the first block so just 
				 * go on.
				 */
				a = a1;
				b = d+1;
			}
		} else {
			a = c+1;
			b = d+1;
		}
	}

}

/*
 * Move any remaining diff blocks align to whitespace boundaries if
 * possible. Adapted from code by wscott in another RTI.
 */
void
align_blocks(string S, int &chg[])
{
	int	a, b;
	int	n;

	n = length(chg);
	a = 0;
	while (1) {
		int	up, down;

		/*
		 * Find a sections of 1's bounded by 'a' and 'b'
		 */
		while ((a < n) && (chg[a] == 0)) a++;
		if (a >= n) return;
		b = a;
		while ((b < n) && (chg[b] == 1)) b++;
		/* b 'might' be at end of file */

		/* Find the maximum distance it can be shifted up */
		up = 0;
		while ((a-up > 0) && (S[a-1-up] == S[b-1-up]) &&
		    (chg[a-1-up] == 0)) {
			++up;
		}
		/* Find the maximum distance it can be shifted down */
		down = 0;
		while ((b+down < n) && (S[a+down] == S[b+down]) &&
		    (chg[b+down] == 0)) {
			++down;
		}
		if (up + down > 0) {
			int	best = 65535;
			int	bestalign = 0;
			int	i;

			/* for all possible alignments ... */
			for (i = -up; i <= down; i++) {
				int	a1 = a + i;
				int	b1 = b + i;
				int	cost = 0;

				/* whitespace at the beginning costs 2 */
				while (a1 < b1 && isspace(S[a1])) {
					cost += 2;
					++a1;
				}

				/* whitespace at the end costs only 1 */
				while (b1 > a1 && isspace(S[b1-1])) {
					cost += 1;
					--b1;
				}
				/* Any whitespace in the middle costs 3 */
				while (a1 < b1) {
					if (isspace(S[a1])) {
						cost += 3;
					}
					++a1;
				}
				/*
				 * Find the alignment with the lowest cost and
				 * if all things are equal shift down as far as
				 * possible.
				 */
				if (cost <= best) {
					best = cost;
					bestalign = i;
				}
			}
			if (bestalign != 0) {
				int	a1 = a + bestalign;
				int	b1 = b + bestalign;

				/* remove old marks */
				while (a < b) chg[a++] = 0;
				/* add new marks */
				while (a1 < b1) chg[a1++] = 1;
				b = b1;
			}
		}
		a = b;
	}
}

/*
 * Align the hunks such that if we find one char in common between
 * changed regions that are longer than one char, we mark the single
 * char as changed even though it didn't. This prevents the sl highlight
 * from matching stuff like foo|b|ar to a|b|alone #the b is common.
 */
hunk[]
align_hunks(string A, string B, hunk[] hunks)
{
	hunk	h, h1, nhunks[];
	int	x, y, lastrl, lastll;

	x = y = lastll = lastrl = 0;
	foreach (h in hunks) {
		if ((((h.li - x) <= h.ll) && ((h.li - x) <= lastll) &&
			isalpha(A[x..h.li - 1])) ||
		    (((h.ri - y) <= h.rl) && ((h.ri - y) <= lastrl) &&
			 isalpha(B[y..h.ri - 1]))) {
			h1.li = x;
			h1.ri = y;
			h1.ll = (h.li - x) + h.ll;
			h1.rl = (h.ri - y) + h.rl;
		} else {
			h1.li = h.li;
			h1.ri = h.ri;
			h1.ll = h.ll;
			h1.rl = h.rl;
		}
		lastll = h1.ll;
		lastrl = h1.rl;
		x = h.li + h.ll;
		y = h.ri + h.rl;
		push(&nhunks, h1);
	}
	return (nhunks);
}

/*
 * Compute the shortest edit distance using the algorithm from
 * "An O(NP) Sequence Comparison Algorithm" by Wu, Manber, and Myers.
 */
hunk[]
diff(string A, string B)
{
	int	M = length(A);
	int	N = length(B);
	int	D;
	int	reverse = (M > N) ? 1: 0;
	int	fp[], path[];
	struct {
		int	x;
		int	y;
		int	k;
	}	pc[];
	struct {
		int	x;
		int	y;
	}	e[];
	int	x, y, ya, yb;
	int	ix, iy;
	int	i, k, m, p, r;
	int	chgA[], chgB[], itmp[];
	string	tmp;
	hunk	hunks[], h;

	if (reverse) {
		tmp = A;
		A = B;
		B = tmp;
		M = length(A);
		N = length(B);
	}

	p = -1;
	fp = lrepeat(M+N+3, -1);
	path = lrepeat(M+N+3, -1);
	m = M + 1;
	D = N - M;

	do {
		p++;
		for (k = -p; k <= (D - 1); k++) {
			ya = fp[m+k-1] + 1;
			yb = fp[m+k+1];
			if (ya > yb) {
				fp[m+k] = y = snake(A, B, k, ya);
				r = path[m+k-1];
			} else {
				fp[m+k] = y = snake(A, B, k, yb);
				r = path[m+k+1];
			}
			path[m+k] = length(pc);
			push(&pc, {y - k, y, r});
		}
		for (k = D + p; k >= (D + 1); k--) {
			ya = fp[m+k-1] + 1;
			yb = fp[m+k+1];
			if (ya > yb) {
				fp[m+k] = y = snake(A, B, k, ya);
				r = path[m+k-1];
			} else {
				fp[m+k] = y = snake(A, B, k, yb);
				r = path[m+k+1];
			}
			path[m+k] = length(pc);
			push(&pc, {y - k, y, r});
		}
		ya = fp[m+D-1] + 1;
		yb = fp[m+D+1];
		if (ya > yb) {
			fp[m+D] = y = snake(A, B, D, ya);
			r = path[m+D-1];
		} else {
			fp[m+D] = y = snake(A, B, D, yb);
			r = path[m+D+1];
		}
		path[m+D] = length(pc);
		push(&pc, {y - D, y, r});
	} while (fp[m+D] < N);
	r = path[m+D];
	e = {};
	while (r != -1) {
		push(&e, {pc[r].x, pc[r].y});
		r = pc[r].k;
	}

	ix = iy = 0;
	x = y = 0;
	chgA = lrepeat(M, 0);
	chgB = lrepeat(N, 0);
	for (i = length(e)-1; i >= 0; i--) {
		while (ix < e[i].x || iy < e[i].y) {
			if (e[i].y - e[i].x > y - x) {
				chgB[iy] = 1;
				iy++; y++;
			} else if (e[i].y - e[i].x < y - x) {
				chgA[ix] = 1;
				ix++; x++;
			} else {
				ix++; x++; iy++; y++;
			}
		}
	}
	if (reverse) {
		tmp = A;
		A = B;
		B = tmp;
		itmp = chgA;
		chgA = chgB;
		chgB = itmp;
	}
	M = length(A);
	N = length(B);

	/* Now we need to minimize the changes by closing gaps */
	shrink_gaps(A, &chgA);
	shrink_gaps(B, &chgB);
	align_blocks(A, &chgA);
	align_blocks(B, &chgB);

	/* edit script length: D + 2 * p */
	for (x = 0, y = 0; (x < M) || (y < N); x++, y++) {
		if (((x < M) && chgA[x]) || ((y < N) && chgB[y])) {
			h.li = x;
			h.ri = y;
			for (; (x < M) && chgA[x]; x++);
			for (; (y < N) && chgB[y]; y++);
			h.ll = x - h.li;
			h.rl = y - h.ri;
			push(&hunks, h);
		}
	}
	hunks = align_hunks(A, B, hunks);
	return(hunks);
}

int
snake(string A, string B, int k, int y)
{
	int	x;
	int	M = length(A);
	int	N = length(B);

	x = y - k;
	while ((x < M) && (y < N) && (A[x] == B[y])) {
		x++;
		y++;
	}
	return (y);
}

int
slhSkip(hunk hunks[], int llen, string left, int rlen, string right)
{
	/* 
	 * If the subline highlight is more than this fraction
	 * of the line length, skip it. 
	 */
	float	hlfactor = gc("hlPercent");
	/*
	 * If the choppiness is more than this fraction of 
	 * line length, skip it.
	 */
	float	chopfactor = gc("chopPercent");

	/*
	 * Highlighting too much? Don't bother.
	 */
	if (llen > (hlfactor*length(left)) ||
	    rlen > (hlfactor*length(right))) {
		return (1);
	}
	/*
	 * Too choppy? Don't bother
	 */
	if ((length(hunks) > (chopfactor*length(left))) ||
	    (length(hunks) > (chopfactor*length(right)))) {
		return (1);
	}
	return (0);
}

// Do subline highlighting on two side-by-side diff widgets.
void
highlightSideBySide(widget left, widget right, string start, string stop, int prefix)
{
	int	i, line;
	string	llines[] = split(/\n/, (string)Text_get(left, start, stop));
	string	rlines[] = split(/\n/, (string)Text_get(right, start, stop));
	hunk	hunks[], h;
	int	llen, rlen;
	int	loff, roff;
	int	allspace;
	string	sl, sr;

	line = idx2line(start);
	for (i = 0; i < length(llines); ++i, ++line) {
		if ((llines[i][0..prefix] == " ") ||
		    (rlines[i][0..prefix] == " ")) continue;
		hunks = diff(llines[i][prefix..END], rlines[i][prefix..END]);
		unless (defined(hunks)) continue;
		llen = rlen = 0;
		allspace = 1;
		foreach (h in hunks) {
			llen += h.ll;
			rlen += h.rl;
			sl = llines[i][prefix+h.li..prefix+h.li+h.ll-1];
			sr = rlines[i][prefix+h.ri..prefix+h.ri+h.rl-1];
			if (sl != "") allspace = allspace && isspace(sl);
			if (sr != "") allspace = allspace && isspace(sr);
		}
		unless (allspace) {
			if (slhSkip(hunks,
			    llen, llines[i], rlen, rlines[i])) continue;
			foreach (h in hunks) {
				Text_tagAdd(left, "highlightold",
				    "${line}.${prefix + h.li}",
				    "${line}.${prefix + h.li + h.ll}");
				Text_tagAdd(right, "highlightnew",
				    "${line}.${prefix + h.ri}",
				    "${line}.${prefix + h.ri + h.rl}");
			}
		} else {
			loff = roff = 0;
			foreach (h in hunks) {
				h.li += loff;
				h.ri += roff;
				sl = Text_get(left,
				    "${line}.${prefix + h.li}",
				    "${line}.${prefix + h.li + h.ll}");
				sl = String_map({" ", "\u2423"}, sl);
				loff += length(sl);
				Text_tagAdd(left, "userData",
				    "${line}.${prefix + h.li}",
				    "${line}.${prefix + h.li + h.ll}");
				Text_insert(left,
				    "${line}.${prefix + h.li + h.ll}",
				    sl, "highlightsp bkMetaData");
				sr = Text_get(right,
				    "${line}.${prefix + h.ri}",
				    "${line}.${prefix + h.ri + h.rl}");
				sr = String_map({" ", "\u2423"}, sr);
				roff += length(sr);
				Text_tagAdd(right, "userData",
				    "${line}.${prefix + h.ri}",
				    "${line}.${prefix + h.ri + h.rl}");
				Text_insert(right,
				    "${line}.${prefix + h.ri + h.rl}",
				    sr, "highlightsp bkMetaData");
			}
		}
	}
}

// Do subline highlighting on stacked diff output in a single text widget.
void
highlightStacked(widget w, string start, string stop, int prefix)
{
	string	line;
	string	lines[];
	int	l = 0, hunkstart = 0;
	string	addlines[], sublines[];

	lines = split(/\n/, (string)Text_get(w, start, stop));
	/*
	 * Since the diffs are stacked, we don't want to highlight regions
	 * that are too big.
	 */
	//	if (length(lines) > 17) return;
	foreach (line in lines) {
		++l;
		if (line[0] == "+") {
			push(&addlines, line[prefix..END]);
			if (!hunkstart) hunkstart = l;
			if (l < length(lines)) continue;
		}
		if (line[0] == "-") {
			push(&sublines, line[prefix..END]);
			if (!hunkstart) hunkstart = l;
			if (l < length(lines)) continue;
		}
		if (defined(addlines) && defined(sublines)) {
			int	i;
			hunk	h, hunks[];
			int	lineA, lineB;
			int	llen, rlen;

			for (i = 0; hunkstart < l; ++hunkstart, ++i) {
				unless (defined(addlines[i])) break;
				unless (defined(sublines[i])) break;
				if (strlen(sublines[i]) > 1000 ||
				    strlen(addlines[i]) > 1000) break;
				hunks = diff(sublines[i], addlines[i]);
				lineA = hunkstart;
				lineB = hunkstart + length(sublines);
				unless (defined(hunks)) continue;
				llen = rlen = 0;
				foreach (h in hunks) {
					llen += h.ll;
					rlen += h.rl;
				}

				if (slhSkip(hunks,
				    llen, sublines[i], rlen, addlines[i])) {
					continue;
				}

				foreach (h in hunks) {
					Text_tagAdd(w, "highlightold",
					    "${lineA}.${h.li+prefix}",
					    "${lineA}.${h.li + h.ll +prefix}");
					Text_tagAdd(w, "highlightnew",
					    "${lineB}.${h.ri + prefix}",
					    "${lineB}.${h.ri + h.rl +prefix}");
				}
			}
		}
		hunkstart = 0;
		addlines = undef;
		sublines = undef;
	}
}

// getUserText
//
// Get data from a text widget as it actually is from the user. This means
// hiding any special characters or other bits we've inserted into the user's
// view and just returning them the real data.
//
// Currently this is only used for highlighted spaces as a result of the
// subline highlighting code, but this is where we want to add stuff in
// the future anytime we alter the user's view of the data.
string
getUserText(widget w, string idx1, string idx2)
{
	string	data;

	// Hide any BK characters we've inserted into the view and
	// show the actual user data as it was inserted.
	Text_tagConfigure(w, "userData", elide: 0);
	Text_tagConfigure(w, "bkMetaData", elide: 1);
	data = Text_get(w, displaychars: idx1, idx2);
	Text_tagConfigure(w, "userData", elide: 1);
	Text_tagConfigure(w, "bkMetaData", elide: 0);
	return (data);
}

/*
 * Windows gui doesn't have a stdout and stderr.
 * A straight system("foo") won't run foo because of this.
 * So put in a string and let the user know it happened.
 */
int
bk_system(string cmd)
{
	string	out, err;
	int	rc;

	if (!defined(rc = system(cmd, undef, &out, &err))) {
		tk_messageBox(title: "bk system",
			      message: "command: ${cmd}\n" . stdio_lasterr);
		return (undef);
	}
	if ((defined(out) && (out != "")) ||
	    (defined(err) && (err != ""))) {
		if (defined(out)) out = "stdout:\n" . out;
		if (defined(err)) err = "stderr:\n" . err;
		if (rc) {
			bk_error("bk system",
				 "command:\n${cmd}\n"
				 "${out}"
				 "${err}");
		} else {
			bk_message("bk system",
				 "command:\n${cmd}\n"
				 "${out}"
				 "${err}");
		}
	}
	return (rc);
}

/*
 * given "line.col" return just line
 */
int
idx2line(string idx)
{
	return ((int)split(/\./, idx)[0]);
}

void
configureDiffWidget(string app, widget w, ...args)
{
	string	which = args[0];

	// Old diff tag.
	Text_tagConfigure(w, "oldDiff",
	    font: gc("${app}.oldFont"),
	    background: gc("${app}.oldColor"));

	// New diff tag.
	Text_tagConfigure(w, "newDiff",
	    font: gc("${app}.newFont"),
	    background: gc("${app}.newColor"));

	if (defined(which)) {
		string	oldOrNew = which;

		// Standard diff tag.
		Text_tagConfigure(w, "diff",
		    font: gc("${app}.${oldOrNew}Font"),
		    background: gc("${app}.${oldOrNew}Color"));

		// Active diff tag.
		if (!gc("${app}.activeNewOnly") || oldOrNew == "new") {
			oldOrNew[0] = String_toupper(oldOrNew[0]);
			Text_tagConfigure(w, "d",
			    font: gc("${app}.active${oldOrNew}Font"),
			    background: gc("${app}.active${oldOrNew}Color"));
		}
	}

	// Highlighting tags.
	Text_tagConfigure(w, "select", background: gc("${app}.selectColor"));
	Text_tagConfigure(w, "highlightold",
			  background: gc("${app}.highlightOld"));
	Text_tagConfigure(w, "highlightnew",
			  background: gc("${app}.highlightNew"));
	Text_tagConfigure(w, "highlightsp",
	    background: gc("${app}.highlightsp"));
	Text_tagConfigure(w, "userData", elide: 1);
	Text_tagConfigure(w, "bkMetaData", elide: 0);

	// Message tags.
	Text_tagConfigure(w, "warning", background: gc("${app}.warnColor"));
	Text_tagConfigure(w, "notice", background: gc("${app}.noticeColor"));

	// Various other diff tags.
	Text_tagConfigure(w, "empty", background: gc("${app}.emptyBG"));
	Text_tagConfigure(w, "same", background: gc("${app}.sameBG"));
	Text_tagConfigure(w, "space", background: gc("${app}.spaceBG"));
	Text_tagConfigure(w, "changed", background: gc("${app}.changedBG"));

	if (defined(which)) {
		Text_tagConfigure(w, "minus",
		    background: gc("${app}.${which}Color"));
		Text_tagConfigure(w, "plus",
		    background: gc("${app}.${which}Color"));
		if (!gc("${app}.activeNewOnly") || which == "new") {
			Text_tagRaise(w, "d");
		}
	}

	Text_tagRaise(w, "highlightold");
	Text_tagRaise(w, "highlightnew");
	Text_tagRaise(w, "highlightsp");
	Text_tagRaise(w, "sel");
}

private int	debug{string};
private	FILE	debugf = undef;

void
debug_enter(string cmd, string op)
{
	unless (cmd) return;

	op = op;
	debug{cmd} = Clock_microseconds();
	fprintf(debugf, "ENTER ${cmd}\n");
	flush(debugf);
}

void
debug_leave(string cmd, int code, string result, string op)
{
	float	t;

	unless (cmd) return;

	op = op;
	code = code;
	result = result;
	if (defined(debug{cmd})) {
		t = Clock_microseconds() - debug{cmd};
		undef(debug{cmd});
	}

	if (defined(t)) {
		fprintf(debugf, "LEAVE ${cmd} (${t} usecs)\n");
	} else {
		fprintf(debugf, "LEAVE ${cmd}\n");
	}
	flush(debugf);
}

void
debug_init(string var)
{
	string	proc, procs[];

	unless (defined(var) && length(var)) return;
	if (var =~ m|^/|) debugf = fopen(var, "w");
	unless (defined(debugf)) debugf = stderr;

	procs = Info_procs("::*");
	foreach (proc in procs) {
		if (proc =~ /^::auto_/) continue;
		if (proc =~ /^::debug_/) continue;
		if (proc =~ /^::unknown/) continue;
		if (proc =~ /^::fprintf/) continue;
		Trace_addExec(proc, "enter", &debug_enter);
		Trace_addExec(proc, "leave", &debug_leave);
	}
}

string
bk_repogca(int standalone, string url, string &err)
{
	string	gca;
	string	opts = "--only-one";

	if (standalone) opts .= " -S";
	if (url && length(url)) opts .= " '${url}'";
	if (system("bk repogca ${opts}", undef, &gca, &err) != 0) {
		return (undef);
	}
	return (trim(gca));
}

void
bk_message(string title, string message)
{
	if (defined(getenv("BK_REGRESSION"))) {
		puts("stdout", message);
	} else {
		tk_messageBox(title: title, message: message);
	}
}

void
bk_error(string title, string message)
{
	if (defined(getenv("BK_REGRESSION"))) {
		puts("stderr", message);
	} else {
		tk_messageBox(title: title, message: message);
	}
}

void
bk_die(string message, int exitCode)
{
	bk_message("BitKeeper", message);
	exit(exitCode);
}

void
bk_dieError(string message, int exitCode)
{
	bk_error("BitKeeper Error", message);
	exit(exitCode);
}

void
bk_usage()
{
	string	usage;
	string	tool = basename(Info_script());

	system("bk help -s ${tool}", undef, &usage, undef);
	puts(usage);
	exit(1);
}
#lang tcl
# Copyright 2000,2003-2005,2011,2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# 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 searchdisable {} \
{
	global search

	searchbuttons both disabled
	$search(menu) configure -state disabled
	$search(text) configure -state 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
}

# initiates a new search on the given string starting at the given index
proc searchnew {direction string {startIndex ""}} {
	global search 

	search $direction

	$search(text) delete 0 end
	$search(text) insert 0 $string

	if {$startIndex != ""} {
		set search(start) $startIndex
	} elseif {$search(dir) == "?"} {
		set search(start) "end"
	} else {
		set search(start) "1.0"
	}

	searchstring
}

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) == ":")} {
		set i [$search(widget) index "end-1c linestart"]
		set end [lindex [split $i .] 0]
		if {$string == "end" || $string == "last"} {
			set string $end
		}
			
		if {[string match {[0-9]*} $string]} {
			if {[$search(widget) compare "$string.0" > "end-1c"]} {
				set msg "beyond end of data"
				$search(status) configure -text $msg
			} else {
				$search(widget) tag remove search 1.0 end
				$search(widget) tag add search \
				    "$string.0" "$string.end+1c"
				$search(widget) tag raise search
				searchsee $string.0
			}
		} 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) TEntry]
}

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 env

	search_init $w $s

	set prevImage [image create photo \
			   -file $env(BK_BIN)/gui/images/previous.gif]
	set nextImage [image create photo \
			   -file $env(BK_BIN)/gui/images/next.gif]
	ttk::label $search(plabel) -width 11 -textvariable search(prompt)

	# XXX: Make into a pulldown-menu! like is sccstool
	ttk::menubutton $search(menu) -text "Search" -menu $search(menu).menu
	    set m [menu $search(menu).menu]
	    if {$gc(aqua)} {$m configure -tearoff 0}
	    $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
	    }
	ttk::entry $search(text) -width 20
	ttk::button $search(prev) -image $prevImage -state disabled -command {
	    searchdir ?
	    searchnext
	}
	ttk::button $search(next) -image $nextImage -state disabled -command {
	    searchdir /
	    searchnext
	}
	ttk::button $search(clear) -text "Clear search" -state disabled \
	    -command { clearOrRecall }
	ttk::label $search(status) -width 20

	pack $search(menu) -side left -anchor w
	pack $search(text) -side left -padx 2
	pack $search(prev) -side left -padx 1
	pack $search(clear) -side left -padx 1
	pack $search(next) -side left -padx 1
	pack $search(status) -side left -expand 1 -fill x -padx {2 0}

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

proc example_main_widgets {} \
{
	global gc app
	#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 $gc($app.searchColor) -relief groove -borderwid 0
}

# Copyright 2002-2003,2015-2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file contains code to read and write application-specific state
# information
#
# usage: ::appState load "appname" arrayVariableName
#          fills array with app-specific keys and values
#
#        ::appStore save "appname" arrayVariableName ?version?
#           writes the array data to a persistent store for the
#           given application, in a format consistent with the version
#           number given
#
# A file read by these routines must have a line that looks like
# "data format version 1.0". At a later date we may support other versions
# as necessary.
#

# this is the primary application interface to these functions; all other
# functions are intended to be called only internally by this code
namespace eval ::appState {
	variable filename
	array set filename {}
}

proc ::appState {command app varname} \
{

	catch {uplevel ::appState::$command $app $varname} result
	return $result
}

proc ::appState::load {app varname {filename {}}} \
{

	upvar $varname state

	if {$filename == {}} {
		set filename [::appState::filename $app]
	}

	if {![file exists $filename]} {
		error "file doesn't exist: \"$filename\""

	} elseif {![file readable $filename]} {
		error "file not readable: \"$filename\""
	}

	if {[catch {
		set f [open $filename r]
		# reading the data in one block is much quicker than
		# reading one line at a time...
		set data [split [read $f [file size $filename]] \n]
		close $f
	} result]} {
		error "unexpected error: $result"
	}
	
	set version [::appState::getVersion data]

	set versionCommands [info commands ::appState::load-$version]

	if {[llength $versionCommands] == 1} {
		set state(Version) $version
		return [::appState::load-$version data state]
	} else {
		return 0
	}
}

proc ::appState::load-1.0 {datavar statevar} \
{
	upvar $datavar data
	upvar $statevar state

	set last [expr {[llength $data] - 1}]
	for {set i 0} {$i <= $last} {incr i} {
		set item [lindex $data $i]
		if {[regexp {^define ([^ ]+) (.*)} $item -> key value]} {
			set key [string trim $key]
			set value [string trim $value]
			set state($key) $value
		}
	}
}

proc ::appState::filename {app} \
{
	global tcl_platform
	variable filename

	# N.B. 'bk dotbk' has the side effect of moving the old config
	# files to the new location (old=prior to 3.0.2). 
	if {![info exists filename($app)]} {
		set new $app.rc
		if {[string equal $::tcl_platform(platform) "windows"]} {
			set old [file join _bkgui.d $app.rc]
		} else {
			set old [file join .bkgui.d $app.rc]
		}
		set filename($app) [exec bk dotbk $old $new]
	}

	return $filename($app)
}

proc ::appState::save {app statevar {version 1.0} {filename ""}} \
{
	upvar $statevar state

	if {$filename == ""} {
		set filename [::appState::filename $app]
	}

	# make sure the directory exists; if not, attempt to create it
	# Note that "file mkdir" is smart enough to do nothing if the
	# directory already exists, but it will throw an error if a file
	# exists with the same name as the directory.
	if {[catch {file mkdir [file dirname $filename]} result]} {
		error "unable to create directory:\
		       \"[file dirname $filename]\""
	}

	# If the directory still doesn't exist, punt
	if {![file isdirectory [file dirname $filename]]} {
		error "not a directory: \
		       \"[file dirname $filename]\""

	}

	# Danger Will Robinson! If you change the format of the line
	# that beings "data format version", be sure to change the
	# the getVersion proc appropriately...
	set f [open $filename w]
	puts $f "# This file was automatically generated by $app\
		 \n# [clock format [clock seconds]]\n\
		 \ndata format version $version\n"

	foreach key [lsort [array names state]] {
		if {[string match $key "Version"]} continue
		if {[string first \n $state($key)]>= 0} {
			puts $f "define $key <<END\n$state($key)\n<<END"
		} else {
			puts $f "define $key $state($key)"
		}
	}
	close $f

	return 1
}

# reads and discards all data up to and including a line that looks 
# like 'data format version <version number>'. The <version number> 
# is returned.
proc ::appState::getVersion {datavar} \
{
	upvar $datavar data

	set version 1.0
	set i 1
	set regex {data format version +(.*)$}
	foreach line $data {
		if {[regexp $regex $line -> version]} {
			# strip all data prior to and including the
			# version statement
			set data [lrange $data $i end]
			break
		}
	}

	return $version
}
	
# Copyright 2004,2014-2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# supportlib - a set of procedures to modify and view a bug database


# 
# Creates a bug overview panel and populates it
# Called from the bugdb button
#
proc bugs:bugs {{cat {open}}} \
{
	bugs:bugList
	bugs:retrieve $cat
}

# 
# update the listbox items
#
proc bugs:updateItems {widget} \
{
	return
}

#
# Populate the table widget with all of the bugs in a specified category
# (open, assigned) Need to add 'closed'
#
# TODO:
#
proc bugs:retrieve {{cat {open}}} \
{
	global w

	if {!(($cat == "open") || ($cat == "closed") || ($cat == "assigned"))} {
		puts stderr "Category \"$cat\" is not a valid category"
		return
	}
	set dspec "-d\$if(:%STATUS:=$cat){:%SEVERITY::%PRIORITY:"
	append dspec ":%BUGID::P::%SUMMARY:}" 
	catch {exec bk root} root
	set bug_loc [file join $root BitKeeper bugs SCCS]

	$w(b_tbl) delete 0 end
	set f [open "| bk prs -hnr+ {$dspec} $bug_loc"]
	while {[gets $f line] >= 0} {
		#puts "line=($line)"
		set l [split $line {}]
		$w(b_tbl) insert end $l
	}
	close $f
	$w(b_title) configure -state normal
	$w(b_title) delete 1.0 end
	switch $cat {
	    "open" {
		$w(b_title) insert end "Bug Report - Open Bugs"
	    }
	    "assigned" {
		$w(b_title) insert end "Bug Report - Assigned Bugs"
	    }
	    "closed" {
		$w(b_title) insert end "Bug Report - Closed Bugs"
	    }
	}
}

proc bugs:newBug {{wid {}}} \
{
	global gc w info bug app

	bugs:bugForm new
}

#
# Set up a floating window for viewing a list of bugs. Bugs are displayed
# in a multi-column list. Double-clicking on an entry brings up a window
# to view or modify the bug.
#
proc bugs:bugList {{wid {}}} \
{
	global gc w info bug app

	if {$wid == ""} {
		set top .__bug_select
		for {set n 2} {[winfo exists $top]} {incr n} {
			set top .__bug_select$n
		}
		toplevel $top
		wm group $top .
		wm transient $top .
		wm title $top "Bugs"
	} else {
		set top $wid
		$wid configure -background $gc(bug.popupBG)
	}

	set w(b_top) $top
	set w(b_tbl) $w(b_top).tbl
	set w(b_txt) $w(b_top).txt
	set w(b_vsb) $w(b_top).vsb
	set w(b_hsb) $w(b_top).hsb
	set w(b_title) $w(b_top).title

	text $w(b_title) \
	    -highlightthickness 0 \
	    -bd 0 \
	    -height 1 \
	    -wrap word \
	    -width 50 \
	    -background $gc($app.textBG) \
	    -foreground $gc($app.textFG)

	text $w(b_txt) \
	    -wrap word \
	    -padx 20 \
	    -highlightthickness 0 \
	    -bd 0 \
	    -width 50 \
	    -height 20 \
	    -background $gc($app.textBG) \
	    -foreground $gc($app.textFG)

	tablelist::tablelist $w(b_tbl) \
	    -columns {2 "Severity"	left
		      2 "Priority"	left
		      0 "Id"		left
		      8 "User"		left
		      80 "Summary"	left} \
	    -labelcommand tablelist::sortByColumn \
	    -yscrollcommand [list $w(b_vsb) set] \
	    -xscrollcommand [list $w(b_hsb) set] \
	    -background $gc($app.textBG) \
	    -selectbackground $gc($app.selectBG) \
	    -selectforeground $gc($app.selectFG) \
	    -width 0
	foreach col {1 3} {
	    $w(b_tbl) columnconfigure $col -background $gc($app.altColumnBG)
	}
	foreach col {0 1} {
	    $w(b_tbl) columnconfigure $col -sortmode integer
	}

	scrollbar $w(b_vsb) -orient vertical \
	    -takefocus 0 -command [list $w(b_tbl) yview] 
	scrollbar $w(b_hsb) -orient horizontal \
	    -takefocus 0 -command [list $w(b_tbl) xview]

	foreach l [$w(b_tbl) labels] {
		bind $l <Configure> [list bugs:updateItems $w(b_tbl)]
	}
	set f $w(b_top).f
	frame $f -bg $gc($app.BG)
	    button $f.new \
		-text "Create New" \
		-command "bugs:newBug"
	    button $f.open \
		-text "View Open" \
		-command "bugs:retrieve open"
	    button $f.assigned \
		-text "View Assigned" \
		-command "bugs:retrieve assigned"
	    button $f.done \
		-text Dismiss \
		-command "destroy $top"
	if {$wid == ""} {
		pack $f.new $f.open $f.assigned $f.done \
		    -side left -expand yes -pady 5 -padx 5
	} else {
		pack $f.new $f.open $f.assigned \
		    -side left -expand yes -pady 5 -padx 5
	}
	set menu $w(b_top).menu
	menu $menu -tearoff no
	$menu add command -label "View Details" \
	    -command [list bugs:bugForm view]
	$menu add command -label "Close Bug" \
	    -command [list bugs:closeBug]
	set body [$w(b_tbl) bodypath]
	#puts "bodypath=($body)"

	bind $body <<Button3>> [list bugs:bugPopupMenu $w(b_top) %X %Y]
	bind $body <Double-1> [list bugs:selectBug $w(b_top) %X %Y]

	grid $w(b_title) -row 0 -column 0 -sticky ew
	grid $w(b_tbl) -row 1 -column 0 -sticky news
	grid $w(b_vsb) -row 1 -column 1 -sticky ns
	grid $w(b_hsb) -row 2 -column 0 -sticky ew
	grid $f -row 3 -column 0 -sticky ew -columnspan 2 -pady 5
	grid rowconfigure $top 0 -weight 0
	grid rowconfigure $top 1 -weight 1
	grid columnconfigure $top 0 -weight 1
} ;# bugList

# 
# Stuff the users choice into a global and reset the color
#
proc setInfo {l widget cat} \
{
	global gc bt_cinfo app

	$widget configure \
	    -text $cat \
	    -bg $gc($app.BG)
	set bt_cinfo($l) "$cat"
	bugs:check_config
	return
}

#
# Display the help information in the bottom text widget.
#
proc showHelp {op widget tag} \
{
	global gc bt_cinfo app fields

	if {![info exists fields($tag)]} { puts "returning $tag"; return }

	setHelpText [getmsg [lindex $fields($tag) 4]]
}

proc setHelpText {text} {
	global gc
	set text [string trim $text]
	# if a line begins with whitespace or a character that looks
	# like a bullet it is appended as a separate line. Otherwise
	# lines are joined so they fill the text widget, which wraps
	# on word boundaries
	set newtext {}
	foreach line [split $text \n] {
		if {[regexp {^[ \t\*\-\+]} $line]} {
			append newtext "\n$line"
		} else {
			append newtext " $line"
		}
	}
	set text [string trim $newtext]
	$gc(v_bf).text configure -state normal
	$gc(v_bf).text delete 1.0 end
	$gc(v_bf).text insert 1.0 $text
	$gc(v_bf).text configure -state disabled
}

proc bugs:bugformMenuBar {} \
{
	global	app gc w
	
	menu .menus -font $gc($app.buttonFont)

	.menus add cascade \
	    -label "File" \
	    -menu .menus.file \
	    -font $gc($app.buttonFont) \
	    -underline 0
	.menus add cascade \
	    -label "Help" \
	    -menu .menus.help \
	    -font $gc($app.buttonFont) \
	    -underline 0
	    
	menu .menus.file -tearoff 0 -font $gc($app.buttonFont)
	    if {0} {
		.menus.file add command \
		    -label "Restart..." \
		    -underline 0 \
		    -command [list bugs:restart]
		.menus.file add separator
	    }
	    .menus.file add command \
		-label "Quit" \
		-underline 0 \
		-accelerator $gc($app.quit) \
		-command [list bugs:bugformDone]
	
	# Help menu
	menu .menus.help \
	    -font $gc($app.buttonFont) \
	    -tearoff 0 
	.menus.help add command \
	    -label "Support Help" \
	    -underline 0 \
	    -command [list exec bk helptool support &]
	
	. configure -menu .menus
}

#
# Zero out all of the fields and let the user start again
#
proc bugs:restart {} \
{
	#
}

#
# Want to give the user an oportunity to save the data if they want to
# exit.
#
proc bugs:bugformDone {} \
{
	exit
}

#
# Create the container widget that contains the scrolling entry box
# of details about a particular bug
#
proc bugs:bugForm {wid {purpose {view}}} \
{
	global	gc w info bug app order fields bt_cinfo

	if {$wid == ""} {
		set top .__bug_view
		for {set n 2} {[winfo exists $top]} {incr n} {
			set top .__bug_view$n
		}
		toplevel $top
		wm group $top .
		wm transient $top .
		wm title $top "Bugs"
		set w(v_bugs) $top
	} elseif {$wid == "."} {
		set top "."
		set w(v_bugs) ""
		$wid configure -background $gc(bug.popupBG)
	}

	if {$purpose == "view"} {
		wm title $top "Support Request - $bug(id)"
	} else {
		wm title $top "Support Request - New"
	}
	wm geometry $top +100+100

	set w(v_frame) $w(v_bugs).frame
	set w(v_c) $w(v_frame).c
	set w(v_vsb) $w(v_frame).vsb
	set w(v_hsb) $w(v_frame).hsb

	bugs:bugformMenuBar

	frame $w(v_frame) -borderwidth 0 -highlightthickness 0
	    canvas $w(v_c) -width 100 -height 100 -background $gc($app.BG) \
	        -highlightthickness 0 \
		-xscrollcommand [list $w(v_hsb) set] \
		-yscrollcommand [list $w(v_vsb) set]
	    scrollbar $w(v_vsb) -orient vertical \
	        -takefocus 0 -command [list $w(v_frame).c yview] 
	    scrollbar $w(v_hsb) -orient horizontal \
	        -takefocus 0 -command [list $w(v_frame).c xview]
	    grid $w(v_c) $w(v_vsb) -sticky news
	    grid $w(v_hsb) -sticky news
	    grid rowconfigure $w(v_frame) 0 -weight 1
	    grid columnconfigure $w(v_frame) 0 -weight 1

	# input (e)ntry frame and (b)utton frame.
	set gc(v_bf) $w(v_bugs).f
	set gc(v_ef) [frame $w(v_c).f -bd 0 -bg $gc($app.BG)]
	#set gc(v_ef) [frame $w(v_c).f -bd 0 -bg black]

	frame $gc(v_bf) -bg $gc($app.BG)
	    button $gc(v_bf).submit -text "Submit" -width 10 \
		-state disabled \
		-command "bugs:doSubmit"
	    button $gc(v_bf).update -text "Update" -width 10 \
		-command "bugs:updateBug"
	    button $gc(v_bf).done -text "Quit" -width 10 \
		-command "destroy $top; exit"
	    # the width of this widget is relatively inconsequential;
	    # it just needs to be smaller than the entry widgets. It
	    # will expand to fill any extra space. If it's too large,
	    # however, it will end up controlling the width of the GUI
	    # which is undesirable.
	    text $gc(v_bf).text -bg $gc($app.BG) \
	        -font $gc($app.buttonFont) \
	        -wrap word \
	        -borderwidth 2 -relief groove \
		-width 40 -height 6 -state disabled

	pack $gc(v_bf).text -side left \
	    -padx 4 -pady 10 -expand yes -fill both

	# for aesthetic reasons we want the text widget to be at least
	# 6 lines tall; if a description is longer than that we want
	# to make sure it's wholly visible.
	set maxlines 6
	foreach field [array names fields] {
		set help [string trim [lindex $fields($field) 4]]
		set lines [llength [split $help \n]]
		set maxlines [expr {$lines > $maxlines ? $lines : $maxlines}]
	}
	$gc(v_bf).text configure -height $maxlines

	if {$purpose == "view"} {
		pack $gc(v_bf).update $gc(v_bf).done \
		    -side left -expand yes -pady 5 -padx 5
	} else {
		pack $gc(v_bf).submit $gc(v_bf).done \
		    -side top -expand yes -pady 5 -padx 5
	}
	grid $w(v_frame) -row 0 -column 0 -sticky news -pady 3 -padx 0
	grid $gc(v_bf) -row 1 -column 0 -sticky ew 
	grid rowconfigure $top 0 -weight 1
	grid rowconfigure $top 1 -weight 0 
	grid rowconfigure $gc(v_bf) 0 -weight 0
	grid columnconfigure $top 0 -weight 1 -pad 20

	$top config -background $gc($app.BG)
	$w(v_c) create window 4 4 -anchor nw -window $gc(v_ef)

	set order $fields(_order)
	foreach l [split $order] {
		if {$l == ""} continue
		if {![info exists fields($l)]} {
			puts stderr "Bad identifier in _order"
			exit
		}
		set widget [lindex $fields($l) 0]
		set gc(v_ef_$widget) "$gc(v_ef).l_${widget}"
		set gc(v_ef_l_$widget) "$gc(v_ef).${widget}"
		set label [lindex $fields($l) 1]
		set wtype [lindex [lindex $fields($l) 3] 0]
		set dim [lindex [lindex $fields($l) 3] 1]
		set row [lindex [lindex $fields($l) 2] 0]
		set col [lindex [lindex $fields($l) 2] 1]
		set span [lindex [lindex $fields($l) 2] 2]
		set state [lindex $fields($l) 5]
		#puts "row=($row) col=($col) span=($span)"
		label $gc(v_ef_l_$widget) -text "${label}:" \
		    -bg $gc($app.BG) \
		    -font $gc($app.noticeFont)
		switch $wtype {
		    "text" {
			set width [lindex $dim 0]
			set height [lindex $dim 1]
			text $gc(v_ef_$widget) -height $height \
			    -font $gc($app.fixedFont) \
			    -borderwidth 1 \
			    -wrap none \
			    -width $width -bg $gc($app.entryColor)
			bind $gc(v_ef_$widget) <FocusIn> \
			    "showHelp enter $widget $l"
		    }
		    "entry" {
			set width [lindex $dim 0]
			entry $gc(v_ef_$widget) \
			    -font $gc($app.fixedFont) \
			    -borderwidth 1 \
			    -width $width -bg $gc($app.entryColor)
			bind $gc(v_ef_$widget) <FocusIn> \
			    "showHelp enter $widget $l"
		    }
		    "dropdown" {
			set gc(bt_$widget) $widget
			set bt_cinfo($l) ""
			menubutton $gc(v_ef_$widget) \
			    -borderwidth 1 \
			    -indicatoron 1 \
			    -font $gc($app.fixedFont) \
			    -relief raised \
			    -bg $gc($app.BG) \
			    -text "Select a $label" \
			    -state normal \
			    -width 18 \
			    -menu $gc(v_ef_$widget).menu
			set cmenu [menu $gc(v_ef_$widget).menu -tearoff 0]
			for {set j 0} {$j < [llength $dim]} {incr j} {
				set item [lindex $dim $j]
				set label [lindex [split $item] 0]
				$cmenu add command -label $item -command \
				    "setInfo $l $gc(v_ef_$widget) $label"
			}
			bind $gc(v_ef_$widget) <Enter> \
			    "showHelp enter $widget $l"
		    }
		}
		
		# Highlight Mandatory fields and enforce checking
		if {[lsearch -exact $fields(_mandatory) $l] > -1} {
			$gc(v_ef_$widget) config -bg $gc($app.mandatoryColor)
			if {$wtype == "text" || $wtype == "entry"} {
				bind $gc(v_ef_$widget) <KeyRelease> {
					bugs:check_config
				}
			} elseif {$wtype == "dropdown"} {
				bind $gc(v_ef_$widget) <ButtonRelease> { 
					bugs:check_config
				}
			}
		}
			
		grid $gc(v_ef_l_$widget) -row $row \
		    -column [expr {$col * 2}] \
		    -sticky w -padx 1 -pady 1
		grid $gc(v_ef_$widget) -row $row \
		    -column [expr {($col * 2) + 1}] \
		    -columnspan $span \
		    -sticky w -padx 1 -pady 1

	}
	# both this and the following update are required to get the 
	# GUI to start up the right size. When testing don't forget
	# to blow away your .rc file so you aren't picking up a saved
	# geometry value.
	update idletasks
	set height [winfo reqheight $gc(v_ef)]
	set width [winfo reqwidth $gc(v_ef)]
	incr height 8
	$w(v_c) configure -height $height -width $width
	$w(v_c) config -scrollregion "0 0 $width $height"
	update idletasks

	bugs:populateInfo

	# this must be done after populateInfo so that proc doesn't
	# have to dork with widget states. We might also want to 
	# consider making readonly fields writable if they have no	
	# pre-filled data
	set order $fields(_order)
	foreach l [split $fields(_order)] {
		if {$l == ""} continue
		set name [lindex $fields($l) 0]
		set state [lindex $fields($l) 5]
		set widget $gc(v_ef_$name)
		set label $gc(v_ef_l_$name)
		if {$state == "readonly"} {
			$widget configure -state disabled
			$label configure -font $gc($app.buttonFont)
		}
	}

	bind Text <Tab> {continue}
	bind Text <Shift-Tab> {continue}
	bind . <Control-q> { bugs:bugformDone }

	if {$purpose == "view"} { bugs:displayBug $gc(v_ef) $type}
	focus $w(v_bugs).frame.c
}

proc bugs:check_config {} \
{
	global w gc

	$gc(v_bf).submit configure -state disabled
	set summary [string trim [$gc(v_ef_summary) get]]
	if {$summary == ""} { return }

	$gc(v_bf).submit configure -state normal
	return
}

#
# display the OS and bk release in the appropriate fields.
# default to bitkeeper-support@bitkeeper.com
#
proc bugs:populateInfo {} \
{
	global	gc w info bug app order fields bt_cinfo

	$gc(v_ef_projemail) insert 1 "bitkeeper-support@bitkeeper.com"
	$gc(v_ef_project) insert 1 "BitKeeper"
	if {[info exists bt_cinfo(projemail)]} {
		   $gc(v_ef_projemail) delete 0 end
		   $gc(v_ef_projemail) insert 1 $bt_cinfo(projemail)
	}
	if {[info exists bt_cinfo(project)]} {
		   $gc(v_ef_project) delete 0 end
		   $gc(v_ef_project) insert 1 $bt_cinfo(project)
	}
	catch {exec bk getuser} user
	catch {exec bk gethost} host
	set submitter "$user@$host"
	$gc(v_ef_submitter) insert 1 $submitter
	catch {exec bk version | head -1} version
	$gc(v_ef_release) insert 1 $version
	catch {exec uname -a} os
	$gc(v_ef_os) insert 1 $os
}

#
# Create the text widgets that display the details on a specific bug.
#
proc bugs:displayBug {f type} \
{
	global w gc info bug

	set dspec "-d\$if(:%BUGID:=$bug(id)){Severity: :%SEVERITY:\\n\\n"
	append dspec "Priority: :%PRIORITY:\\n\\n"
	append dspec "Submitter: :P:\\n\\n"
	append dspec "Summary: :%SUMMARY:\\n\\n"
	append dspec "Program: :%PROGRAM:\\n\\n"
	append dspec "Description: :%DESCRIPTION:\\n\\n"
	append dspec "Suggestion: :%SUGGESTION:\\n\\n"
	append dspec "Interest: :%INTEREST:\\n\\n"
	append dspec "Updates: :%UPDATES:\\n}"

	set dspec "-d\$if(:%BUGID:=$bug(id)){:%SEVERITY:"
	append dspec ":%PRIORITY:"
	append dspec ":P:"
	append dspec ":%SUMMARY:"
	append dspec ":%PROGRAM:"
	append dspec ":%DESCRIPTION:"
	append dspec ":%SUGGESTION:"
	append dspec ":%INTEREST:"
	append dspec ":%UPDATES:}"

	catch {exec bk root} root
	set bug_loc [file join $root BitKeeper bugs SCCS]

	set fd [open "| bk prs -hnr+ {$dspec} $bug_loc"]
	set line [read $fd]
	set l [split $line {}]
	set i 0
	foreach item [list s p sub sum prog desc sug int updates] {
		set d [lindex $l $i]
		set widget $f.$item
		#puts "i=($i) item=($item) widget=($widget)\n\td=($d)\n==\n"
		catch {$widget insert 1.0 $d}
		incr i
	}
	if {0} {
	    # Display the bug info in a plain text widget
	    while {[gets $fd line] >= 0} {
		    #puts "line=($line)"
		    set l [split $line {}]
		    #binary scan $m axaxa14xA* p s id syn
		    #$w(v_txt) insert end "$line\n"
		    puts "l=($l)"
	    }
	}
	close $fd

} ;# bugs:bugView

#
# uuencode the given attachment
#
# Return codes:
#    0  -- OK
#    1  -- no attachment given
#    2  -- attachment doesn't exist
#
proc bugs:doAttachment {} \
{
	global w gc info bug fields bt_cinfo

	set label [lindex $fields(ATTACHMENT) 0]
	set bt_cinfo(ATTACHMENT) [string trimright [$gc(v_ef_$label) get]]
	if {$bt_cinfo(ATTACHMENT) == ""} { return 1}

	if {![file exists $bt_cinfo(ATTACHMENT)]} {
		displayMessage "File $bt_cinfo(ATTACHMENT) doesn't exists.\n
Please enter the full path to the attachment."
		return 2
	}
	if {![file isfile $bt_cinfo(ATTACHMENT)]} {
		displayMessage "Attached file must be a plain file and not a directory or symlink."
		return 2
	}
	set outfile [tmpfile supportlib]
	set od [open "$outfile" w]
	#catch {exec bk uuencode $bt_cinfo(ATTACHMENT) $outfile > $outfile} err
	set fd [open "|bk uuencode $bt_cinfo(ATTACHMENT) $outfile"]
	set i 0
	while {[gets $fd l] >= 0} {
		incr i
		puts $od $l
		#puts stderr $l
	}
	catch {close $od}
	catch {close $fd}
	set bt_cinfo(ATTACHMENT,lines) $i
	set bt_cinfo(ATTACHMENT,file) $outfile
	#puts "af=($bt_cinfo(ATTACHMENT,file)) l=($bt_cinfo(ATTACHMENT,lines))"
	return 0
}

proc lc {a} \
{
	set i 0; set n 0;
	set l [string length $a]

	#puts "a chars($l)\n($a)"
	while {$i < $l} {
		if {[string index $a $i] == "\n"} {incr n}
		incr i
	}
	return $n
}

proc bugs:doSubmit {} \
{
	global w gc info bug fields bt_cinfo

	set rc [bugs:submitBug]
	if {$rc == 0} { exit }
}

proc bugs:submitBug {} \
{
	global w gc info bug fields bt_cinfo

	set address "bitkeeper-support@bitkeeper.com"
	set attachment 0
	# before doing anything, check the attachment. If not valid,
	# error and return so that the user can update. If we do this
	# in the loop, it is harder to return and clean up...
	set rc [bugs:doAttachment]
	if {$rc == 0} {
		set attachment 1
	} elseif {$rc == 2} {
		return 1
	}

	set bd [tmpfile supportlib]
	catch {file mkdir $bd} err
	if {$err != ""} {displayMessage "$err"}

	set order [lsort $fields(_order)]
	set bugfile [file join $bd "bug"]
	set kvd [open $bugfile "w"]
	puts $kvd "VERSION=2"
	puts $kvd "END_HEADER"

	foreach l [split $order] {
		set wtype [lindex [lindex $fields($l) 3] 0]
		set label [lindex $fields($l) 0]
		switch $wtype {
		    "text" {
			set e [$gc(v_ef_$label) get 1.0 end]
			set bt_cinfo($l) $e
			set len [lc $e]
			puts $kvd "@$l\n$e"
		    }
		    "entry" {
			set e [string trimright [$gc(v_ef_$label) get]]
			set bt_cinfo($l) $e
			if {$l == "ATTACHMENT" && ($attachment == 1)} {
				puts $kvd "@$l"
				set fd [open "$bt_cinfo(ATTACHMENT,file)" r]
				while {[gets $fd line] >= 0} {puts $kvd "$line"}
				catch {close $fd}
			} else {
				puts $kvd "@$l\n$e"
			}
			puts $kvd ""
		    }
		    "dropdown" {
		    	set e $bt_cinfo($l)
			puts $kvd "@$l\n$e\n"
		    }
		} 
		set fd [open [file join $bd "bug.$l"] w]
		fconfigure $fd -translation binary
		puts $fd $e
		catch {close $fd}
	}
	catch {close $kvd}
	catch {file delete $bt_cinfo(ATTACHMENT,file)}

# In 2.1 use the following. In 2.0 use the tcl kvimplode function
#	catch {exec bk _kvimplode $bug } notuse
# When Merging into dev, ask Aaron to merge this if it is not clear.
	#kvimplode $bd $bug
	#puts "bk _mail $bt_cinfo(PROJEMAIL) report $bug"
	
	if {$bt_cinfo(PROJEMAIL) ne ""} {
		set address $bt_cinfo(PROJEMAIL)
	}
	catch {exec bk mail -u http://bitmover.com/cgi-bin/bkdmail -s "SUPPORT: $bt_cinfo(SUMMARY)" $address < $bugfile } 
	catch {exec rm -rf $bd $bugfile} err
	displayMessage "Your support request has been sent. Thank you for taking
the time to fill out this form. "
	return 0
} ;# bugs:submitBug

proc kvimplode {bd bugfile} \
{
	global fields

	set order [lsort $fields(_order)]
}

proc bugs:updateBug {} \
{
	global gc w info bug

	if {![info exists bug(id)] || ($bug(id) == "")} { return }

	#puts "trying-- This cset fixes bugid $bug(id)"
}

proc bugs:closeBug {} \
{
	global gc w info bug

	if {![info exists bug(id)] || ($bug(id) == "")} { return }

	#puts "trying-- This cset fixes bugid $bug(id)"
	.top.comments configure -state normal
	.top.comments insert end "This cset fixes bugid $bug(id)\n"
}

proc bugs:selectBug {wid x y} \
{
	global gc w info bug

	set tbl $w(b_tbl)
	set curSel [$w(b_tbl) curselection]
	if {[llength $curSel] == 0} {
		bell
		return ""
	}
	set menu $w(b_top).menu
	set bug(id) [$tbl cellcget $curSel,2 -text]
	bugs:bugForm view
}

proc bugs:bugPopupMenu {wid x y} \
{
	global gc w info bug

	set tbl $w(b_tbl)
	set curSel [$w(b_tbl) curselection]
	if {[llength $curSel] == 0} {
		bell
		return ""
	}
	set menu $w(b_top).menu
	set bug(id) [$tbl cellcget $curSel,2 -text]
	#puts "bug(id)=($bug(id))"
	tk_popup $menu $x $y
}
# Copyright 2004-2006,2015-2016 BitMover, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# Simple wrapper to start up the gui bugform. 
#

# entry {
#  {widget name} 
#  {label name}
#  {row col span}
#  {{widget type} {dimensions/values}}
#  {information/help text}}
#  {state ("readonly" if the user shouldn't modify the field, or blank)}
#
# _order is needed so that we can display the list in the order
# we want. The array is actually a hash and foreach doesn't guarantee
# an ordering.
#
# Some of the verboseness of this format is due to tk not allowing
# capital names in the widget names. Amy needed the CAPITAL field for
# the kv file. I could have used 'string toupper', but wasn't sure if
# we would be having places where we wanted a different widget name from
# the KVFILE field identifier.
#
# The fields in the _order list need to be in the order that you want
# the tab (next field) to go in. While you can muck with the location
# using the row,col,span field, realize that the tab order is determined
# by when the widgets are layed out.

array set fields {
    _order {SUBMITTER SUMMARY PROGRAM OS RELEASE \
	DESCRIPTION INTEREST ATTACHMENT PROJEMAIL PROJECT}
    _mandatory {SUMMARY}
    SUBMITTER {
	{submitter} {Submitter} {1 0 4} {entry {72 1}}
	{gui-support-submitter}}
    SUMMARY {
	{summary} {Summary} {2 0 4} {entry {72 1}}
	{gui-support-summary}}
    PROGRAM {
	{program} {Program} {3 0 4} {entry {72 1}}
	{gui-support-program}}
    OS {
	{os} {OS} {4 0 4} {entry {72 1}}
	{gui-support-os}
	readonly
    }
    RELEASE {
	{release} {Release} {5 0 4} {entry {72 1} } 
	{gui-support-release}
	readonly
    }
    DESCRIPTION {
	{description} {Description} {6 0 4} {text {72 6}}
	{gui-support-description}}
    INTEREST {
	{interest} {Interest} {8 0 4} {entry {72 1}}
	{gui-support-interest}}
    ATTACHMENT {
	{attachment} {Attachment} {9 0 4} {entry {72}}
	{gui-support-attachment}}
    PROJEMAIL {
	{projemail} {Project Email} {10 0 4} {entry {72 1}}
	{gui-support-projemail}
	readonly
    }
    PROJECT {
	{project} {Project Name} {11 0 4} {entry {72 1}} 
	{gui-support-project}
	readonly
    }
}

proc main {} \
{
	global bt_cinfo State gc

	bk_init
	getConfig support

	loadState support
	bugs:bugForm . new
	restoreGeometry support

	# this arranges for the state to be saved; this binding should be done
	# after the app initializes so the state doesn't get saved if the app
	# terminates unexpectedly.
	bind . <Destroy> {
		if {[string match %W .]} {
			saveState support
		}
	}

	setHelpText [getmsg gui-support-help]
	wm deiconify .
}

main
