#!/usr/bin/wish

# This file contains code to set up the Hanzim main window and the event
# handlers to respond to mouse presses, etc..

#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.  Please see the file "COPYING"
#   for details.  If you have not received it along with this program,
#   please write to the Free Software Foundation, Inc., 59 Temple Place,
#   Suite 330, Boston, MA 02111-1307, USA.

# Comment this out if you want default colors (greys)
#tk_bisque


##############################################################################
############################  CONFIGURATION  #################################
##############################################################################

# The following global variables may be usefully set by the user

# fonts:
#   (Chinese and pinyin fonts are set in fonts.unix and fonts.windows)
# tfontG -- font to use to display ascii
# cdfontG -- font to use for displaying the central definition (usu. smaller)
# bfontG -- font to use for button titles
# pyfontG -- font to use for pinyin (must be a gb2312 (jiantizi) font)
#
# cpheightG -- height (in characters) of the compound scrolling displays
# crheightG -- height (in characters) of the character scrolling displays
# rmheightG -- height (in characters) of the radical pulldown menus
# cspacingG -- how much blank space (in points) to leave above and below
#	      lines in character displays... may need to be adjusted depending
#	      on which Chinese font you use to match evenly with the ascii
#	      display (3 and 1 refer to the Tk text widget properties)
# vtpadxG -- horizontal space in pixels between arrow buttons and radical menus
#
# bwTcolsG -- width of bottom-widget text definition displays, in characters
# cwTcolsG -- width of compound-widget text definition displays, in characters
# mdTcolsG -- width of main text definition display, in characters
# edTcolsG -- width of English definition text displays, in characters
#
# proPyW -- width of pronunciation pinyin/zhuyinfuhao display window

# All of these variables are set independently for each window size:
# "small" (cfontG(small), tfontG(small), etc.), "medium", and "large".
# Chinese and pinyin fonts are NOT set here, but instead in fonts.unix and
# fonts.windows.

##############################################################################
# Fonts and sizes: "-large" setting

set tfontG(large)	"8x13"
set cdfontG(large)	"8x13"
set bfontG(large)	"-Adobe-Helvetica-Bold-R-Normal--16-100-*-*-*-*-*-*"
set crheightG(large)	"9"
set cpheightG(large)	"9"
set rmheightG(large)	"19"
set cspacingcG(large)	"3"
set cspacingtG(large)	"1"
set vtpadxG(large)	"201"

set bwTcolsG(large)	"28"
set cwTcolsG(large)	"32"
set mdTcolsG(large)	"19"
set edTcolsG(large)	"20"

set proPyWG(large)	"20"


##############################################################################
# Fonts and sizes: "-medium" setting

set tfontG(medium)	"7x13"
set cdfontG(medium)	"7x13"
set bfontG(medium)	"-Adobe-Helvetica-Bold-R-Normal--13-100-*-*-*-*-*-*"
set crheightG(medium)	"7"
set cpheightG(medium)	"7"
set rmheightG(medium)	"15"
set cspacingcG(medium)	"3"
set cspacingtG(medium)	"1"
set vtpadxG(medium)	"182"

set bwTcolsG(medium)	"28"
set cwTcolsG(medium)	"32"
set mdTcolsG(medium)	"19"
set edTcolsG(medium)	"20"

set proPyWG(medium)	"18"


##############################################################################
# Fonts and sizes: "-small" setting

set tfontG(small)	"5x8"
set cdfontG(small)	"5x7"
set bfontG(small)	"-Adobe-Helvetica-Bold-R-Normal--12-100-*-*-*-*-*-*"
set crheightG(small)	"8"
set cpheightG(small)	"9"
set rmheightG(small)	"17"
set cspacingcG(small)	"3"
set cspacingtG(small)	"3"
set vtpadxG(small)	"87"

set bwTcolsG(small)	"28"
set cwTcolsG(small)	"32"
set mdTcolsG(small)	"19"
set edTcolsG(small)	"20"

set proPyWG(small)	"15"


##############################################################################
# colors, all settings and systems
set same_hz_bg white		; # same feature, hanzi, background
set same_hz_fg grey8		; # same feature, hanzi, foreground
set same_as_bg grey35		; # same feature, ascii, background
set same_as_fg grey90		; # same feature, ascii, foreground
set comp_hz_bg white		; # compound, hanzi, background
set comp_hz_fg grey8		; # compound, hanzi, foreground
set comp_as_bg grey92		; # compound, ascii, background
set comp_as_fg black		; # compound, ascii, foreground
set rad_hz_bg  white		; # radical table, background
set rad_hz_fg  grey8		; # radical table, foreground
set highlight  grey		; # highlight color (all hanzi areas)


##############################################################################
######################  END OF CONFIGURATION  ################################
##############################################################################


##############################################################################
# Code below gets really ugly at points...

# Parse command line

set ssmode "large"
set cstate "jianhua"
# prmode: 0 = none, 1 = force pinyin, 2= force bopomofo
set prmode 0
set pstate "py"
set hidePron "false"
if { $argc > 0 } {
    foreach arg $argv {
	if {$arg == "-small"} {
	    set ssmode "small"
	} elseif { $arg == "-medium" } {
	    set ssmode "medium"
	} elseif { $arg == "-large" } {
	    set ssmode "large"
	} elseif { $arg == "-traditional" } { 
	    set cstate "fanti"
	} elseif { $arg == "-simplified" } { 
	    set cstate "jianhua"
	} elseif { $arg == "-pinyin" } { 
	    set prmode 1
	    set pstate "py"
	} elseif { $arg == "-bopomofo" } { 
	    set prmode 2
	    set pstate "bpmf"
	} elseif { [string equal -nocase -length 8 $arg "-hidepron"] } {
	    set hidePron "true"
	}
    }
}

# this variable is defined in hanzim.C
source $fontPropsFile

# set fonts, etc. according to desired window size
set cfont	$chineseFont($ssmode)
#set cfontT	$chineseFontT($ssmode)
set pyfont	$pinyinFont($ssmode)
set bpfont	$bpmfFont($ssmode)

# Here's an example of how these might be automatically determined;
# unfortunately, this results in super-ugly "fixed" fonts, and it
# doesn't seem to respond to "family" specifications.
#font create f1 -family -*-*-*-*-*--*-*-*-*-*-*-gb2312.1980-0 -size 24
#puts [font actual f1]
#set cfont f1
#font create f2 -family -*-*-*-*-*--16-*-*-*-*-*-gb2312.1980-0 -size 16
#puts [font actual f2]
#set pyfont f2

set tfont	$tfontG($ssmode)
set cdfont	$cdfontG($ssmode)
set bfont	$bfontG($ssmode)

# We now determine font metrics automatically; if messed-up or lopsided
# formatting results, try tweaking these up or down..
set cfontw [expr [font measure $cfont [yingHanCh]] / 2 + 2]
set cfonth [expr [font metrics $cfont -linespace] + 4]
set tfontw [font measure $tfont "M"]
set tfonth [expr [font metrics $tfont -linespace] + 2]

# finally, some straight settings
set crheight	$crheightG($ssmode)
set cpheight	$cpheightG($ssmode)
set rmheight	$rmheightG($ssmode)
set cspacingc	$cspacingcG($ssmode)
set cspacingt	$cspacingtG($ssmode)
set vtpadx	$vtpadxG($ssmode)

set bwTcols	$bwTcolsG($ssmode)
set cwTcols	$cwTcolsG($ssmode)
set mdTcols	$mdTcolsG($ssmode)
set edTcols	$edTcolsG($ssmode)

set proPyW	$proPyWG($ssmode)

set miniMode	"false"

# TCL/TK BUG NOTES
# - Currently (tcl/tk 8.1a2) the text widget doesn't compensate for
#   width of Chinese character graphically, so must set to triple widths
#   (utf-8 = 3 byte) :( -- yet, internally, the widget understands that 3
#   utf8 bytes map to a single character :)
# -> 8.3 FIXED
# - Scrolling two text widgets of different heights from one scrollbar (in
#   proportion) currently doesn't work correctly, so must embed everything
#   in canvases
# -> Who knows, was probably considered a "feature", but now worked around..


##############################################################################
# (internal) global variables:
set flevel 5			; # frequency level (1=disp most frequent)
set pinyinentry ""		; # text entry variable for pinyin
set mainchar ""			; # main displayed character (utf8 fontstring)
set mainidx [randChar $flevel]	; # index of main displayed character
set radch ""			; # radical of main displayed character (utf8)
set radmn ""			; # meaning of main radical
set remch ""			; # remainder of main displayed character
set rempy ""			; # pronunciation of main remainder
set proch ""			; # a dummy variable used for padding below
set propy ""			; # pronunciation of main character (bottom)
set compy ""			; # pronunciation of other compound part
set history {}			; # history queue (FILO)
set hpos -1			; # history queue position
set maxpos -1			; # history endpos
set MAXHIST 5000		; # maximum queue legth before start deleting
set yhState 0			; # English definitions on/off


##############################################################################
#################################  Widgets  ##################################
##############################################################################

# first, frames
frame .bottom -relief ridge -borderwidth 4
frame .bottom.rad
frame .bottom.rad.m
frame .bottom.rem
frame .bottom.rem.m
frame .bottom.pro
frame .bottom.pro.m
frame .left -relief ridge -borderwidth 4
frame .right -relief ridge -borderwidth 4
frame .central -relief ridge -borderwidth 4
frame .central.buttons -relief ridge -borderwidth 4
frame .verytop
frame .verytop.menus -relief ridge -borderwidth 4

# text and scrollbars for characters and definitions along bottom
label .bottom.rad.m.ch -font $cfont -textvariable radch -pady 2 -relief ridge \
	-height 1 -width 2 -bg $same_hz_bg -fg $same_hz_fg
label .bottom.rad.m.mn -font $bfont -bg $same_hz_bg -fg $same_hz_fg \
	-height 1 -width 20 -textvariable radmn
canvas .bottom.rad.c -yscrollcommand {.bottom.rad.scrollsrad set} \
	-width $cfontw -height [expr ($cfonth+$cspacingc) * $crheight] \
	-background $same_hz_bg -relief sunken -borderwidth 2
canvas .bottom.rad.t -relief sunken -borderwidth 1 -background $same_as_bg \
        -width [expr $tfontw * $bwTcols + 1] \
	-height [expr ($tfonth * 2 + $cspacingt) * $crheight]
scrollbar .bottom.rad.scrollsrad -command \
	"scroll2 \".bottom.rad.c\" \".bottom.rad.t\""

canvas .bottom.spacer1 -width 4 -height 0

label .bottom.rem.m.ch -font $cfont -textvariable remch -pady 2 -relief ridge \
	-height 1 -width 2 -bg $same_hz_bg -fg $same_hz_fg
label .bottom.rem.m.mn -font $pyfont -bg $same_hz_bg -fg $same_hz_fg \
	-height 1 -width 15 -textvariable rempy
canvas .bottom.rem.c -yscrollcommand {.bottom.rem.scrollsrem set} \
	-width $cfontw -height [expr ($cfonth+$cspacingc) * $crheight] \
	-background $same_hz_bg -relief sunken -borderwidth 2
canvas .bottom.rem.t -relief sunken -borderwidth 1 -background $same_as_bg \
        -width [expr $tfontw * $bwTcols + 1] \
	-height [expr ($tfonth * 2 + $cspacingt) * $crheight]
scrollbar .bottom.rem.scrollsrem -command \
	"scroll2 \".bottom.rem.c\" \".bottom.rem.t\""

canvas .bottom.spacer2 -width 4 -height 0

# this first is not used but is left in invisible for space padding
label .bottom.pro.m.ch -font $cfont -textvariable proch -pady 2 -height 1 \
	-width 2
# -relief ridge  -bg $same_hz_bg -fg $same_hz_fg
label .bottom.pro.m.mn -font $pyfont -bg $same_hz_bg -fg $same_hz_fg \
	-height 1 -width $proPyW -textvariable propy
canvas .bottom.pro.c -yscrollcommand {.bottom.pro.scrollspro set} \
	-width $cfontw -height [expr ($cfonth+$cspacingc) * $crheight] \
	-background $same_hz_bg -relief sunken -borderwidth 2
canvas .bottom.pro.t -relief sunken -borderwidth 1 -background $same_as_bg \
        -width [expr $tfontw * $bwTcols + 1] \
	-height [expr ($tfonth * 2 + $cspacingt) * $crheight]
scrollbar .bottom.pro.scrollspro -command \
	"scroll2 \".bottom.pro.c\" \".bottom.pro.t\""

# text and scrollbars for compound definitions on left and right
canvas .left.lcompc -yscrollcommand {.left.scrolllcomp set} -relief sunken \
	-height [expr ($cfonth+$cspacingc) * $cpheight] \
	-width [expr $cfontw * 2] -borderwidth 2 -background $comp_hz_bg
canvas .left.lcompt -relief sunken -borderwidth 1 -background $comp_as_bg \
        -width [expr $tfontw * $cwTcols + 2] \
	-height [expr ($tfonth * 2 + $cspacingt) * $cpheight]
scrollbar .left.scrolllcomp -command \
	"scroll2 \".left.lcompc\" \".left.lcompt\""

canvas .right.rcompc -yscrollcommand {.right.scrollrcomp set} -relief sunken \
	-height [expr ($cfonth+$cspacingc) * $cpheight] \
	-width [expr $cfontw * 2] -borderwidth 2 -background $comp_hz_bg
canvas .right.rcompt -relief sunken -borderwidth 1 -background $comp_as_bg \
        -width [expr $tfontw * $cwTcols + 2] \
	-height [expr ($tfonth *2 + $cspacingt) * $cpheight]
scrollbar .right.scrollrcomp -command \
	"scroll2 \".right.rcompc\" \".right.rcompt\""

# other text areas
#entry .central.pyenter -font $tfont -width 7 -textvariable pinyinentry
entry .central.pyenter -font $pyfont -width 12 -textvariable pinyinentry
label .central.char -font $cfont -relief groove -textvariable mainchar -pady 2
text .central.defn -font $cdfont -width $mdTcols -height 2 -state disabled
#label .central.compy -font $cdfont -width 7 -textvariable compy
# the width of this does not make any particular sense
label .central.compy -font $pyfont -width 12 -textvariable compy \
	-borderwidth 0 -pady 0 -justify center

# buttons and sliders
radiobutton .central.buttons.simp -text "Simplified" -variable cstate \
	    -value jianhua -command "redispchar" -anchor w -font $bfont
radiobutton .central.buttons.trad -text "Traditional" -variable cstate \
	    -value fanti -command "redispchar" -anchor w -font $bfont
button .central.buttons.random -text "Random" -command genrandom -padx 0.5m \
	-font $bfont
button .central.buttons.quit -text "Quit" -command hanziquit -padx 0.5m \
	-font $bfont
scale .central.fscale -from 1 -to 10 -resolution 1 -length 90 \
	-orient horizontal -variable flevel \
	-command "setflevel"

# buttons on top row
button .verytop.back -bitmap "leftArrow" -command backHist -padx 0.5m \
	-font $bfont -state disabled -height 28
button .verytop.forw -bitmap "rightArrow" -command forwHist -padx 0.5m \
	-font $bfont -state disabled -height 28
button .verytop.yinghan -font $cfont -relief raised -command toggleYingHan \
	-text [yingHanCh] -width 3 -pady 0.5m

# stuff for English lookup pop-down section
frame .verybottom
canvas .verybottom.yinghanC -height 34 \
	-xscrollcommand {.verybottom.yinghanS set}
scrollbar .verybottom.yinghanS -orient horizontal -width 9 \
	-command {.verybottom.yinghanC xview}	
pack .verybottom.yinghanC -in .verybottom -side top -fill x
pack .verybottom.yinghanS -in .verybottom -side bottom -fill x

wm title . Hanzim


##############################################################################
################################  Bindings  ##################################
##############################################################################

# Pin-yin entry
bind .central.pyenter <Button-1> {
    .central.pyenter delete 0 end
    if { $pstate == "bpmf" } then {
	.central.pyenter configure -font $pyfont
    }
}
bind .central.pyenter <Return> {
    set res ""
    if { $pstate == "bpmf" } then {
	.central.pyenter configure -font $bpfont
    }
    if { [catch { pyChar $pinyinentry $flevel } res ] } {
	#puts stderr "hanzim: illegal pinyin"
	puts ""
	redispchar
    } else {
	dispchar $res 1
    }
}

# Toggle "mini" mode, where pronunciations are not displayed
bind . <Control-p> "toggleMiniMode"
bind . <Alt-p> "toggleMiniMode"

# Grab Selection
bind . <Control-g> "grabSelection"
bind . <Alt-g> "grabSelection"

# Help
bind . <Control-h> "helpPopup"
bind . <Alt-h> "helpPopup"

# Quit
bind . <Control-q> hanziquit
bind . <Control-c> hanziquit
bind . <Alt-q> hanziquit
bind . <Alt-x> hanziquit


##############################################################################
##############################  Procedures  ##################################
##############################################################################

# Called to reset the display for a particular main character.
proc redispchar {} {
    global mainidx
    setpstate
    dispchar $mainidx 1
}

# Sets pronunciation display state to either "py" (pinyin) or "bpmf" (bopomofo)
# depending on whether in simp or trad mode, unless the variable 'prmode' is
# set to 1 (pinyin) or 2 (bopomofo), which takes precedence.
proc setpstate {} {
    global prmode pstate cstate bpfont pyfont
    if {$prmode == 0} then {
	if {$cstate == "jianhua"} then {
	    set pstate "py"
	    .central.compy configure -font $pyfont
	    .central.pyenter configure -font $pyfont
	    .bottom.rem.m.mn configure -font $pyfont
	    .bottom.pro.m.mn configure -font $pyfont
	} else {
	    set pstate "bpmf"
	    .central.compy configure -font $bpfont
	    .central.pyenter configure -font $bpfont
	    .bottom.rem.m.mn configure -font $bpfont
	    .bottom.pro.m.mn configure -font $bpfont
	}
    }
}

# Toggles between mini mode (no pronunciations displayed) and regular mode.
proc toggleMiniMode {} {
    global miniMode bwTcols cwTcols tfontw vtpadx ssmode crheight cpheight
    # Direct top-level window geometry manipulation code taken out -r 1.27
    if { $miniMode } {
	set bwTcols [expr $bwTcols + 5]
	set cwTcols [expr $cwTcols + 6]
	set miniMode "false"
	set spcW 4
	set vtpadx [expr $vtpadx + 6 * $tfontw]
	# for small and large modes, unshorten other widgets
	if { ($ssmode == "small") || ($ssmode == "large") } {
	    incr crheight 1
	    adjustBwidgHeight
	    if { $ssmode == "large" } {
		incr cpheight 1
		adjustCwidgHeight
	    }
	}
    } else {
	set bwTcols [expr $bwTcols - 5]
	set cwTcols [expr $cwTcols - 6]
	set miniMode "true"
	set spcW 16
	set vtpadx [expr $vtpadx - 6 * $tfontw]
	# for small and large modes, shorten other widgets
	if { ($ssmode == "small") || ($ssmode == "large") } {
	    incr crheight -1
	    adjustBwidgHeight
	    if { $ssmode == "large" } {
		incr cpheight -1
		adjustCwidgHeight
	    }
	}
    }
    # Update text display widths and bottom spacers
    .bottom.rad.t configure -width [expr $tfontw * $bwTcols + 1]
    .bottom.rem.t configure -width [expr $tfontw * $bwTcols + 1]
    .bottom.pro.t configure -width [expr $tfontw * $bwTcols + 1]
    .left.lcompt configure -width [expr $tfontw * $cwTcols + 2]
    .right.rcompt configure -width [expr $tfontw * $cwTcols + 2]
    .bottom.spacer1 configure -width $spcW
    .bottom.spacer2 configure -width $spcW
    # Adjust top bar
    pack configure .verytop.menus -padx $vtpadx
    # Go
    redispchar
}


# Called when the frequency slider dragged (variable set automatically).
proc setflevel {fl} {
    redispchar
}

# Called to display a random character.
proc genrandom {} {
    global flevel
    dispchar [randChar $flevel] 1
}

# Called on exit.
proc hanziquit {} {
    destroy 
    exit
}

# Called to reset the display to the previous main character in the history.
proc backHist {} {
    global hpos maxpos history
    if {$hpos > 0} then {
	incr hpos -1
	if { $hpos == 0 } then { .verytop.back configure -state disabled }
	.verytop.forw configure -state normal
	dispchar [lindex $history $hpos] 0
	}
}

# Called to reset the display to the next main character in the history.
proc forwHist {} {
    global hpos maxpos  history
    if {$hpos < $maxpos} then {
	if { $hpos == [expr $maxpos - 1] } then {
	    .verytop.forw configure -state disabled}
	.verytop.back configure -state normal
	incr hpos
	dispchar [lindex $history $hpos] 0
    }
}


# Adjusts height of compound widgets to reflect current value of cpheight
proc adjustCwidgHeight {} {
    global cpheight cfonth cspacingc tfonth cspacingt
    .left.lcompc configure -height [expr ($cfonth+$cspacingc) * $cpheight]
    .left.lcompt configure -height [expr ($tfonth *2 + $cspacingt) * $cpheight]
    .right.rcompc configure -height [expr ($cfonth+$cspacingc) * $cpheight]
    .right.rcompt configure -height [expr ($tfonth *2+ $cspacingt) * $cpheight]
}

# Adjusts height of bottom widgets to reflect current value of crheight
proc adjustBwidgHeight {} {
    global  crheight cfonth cspacingc tfonth cspacingt
    .bottom.rad.c configure -height \
	[expr ($cfonth+$cspacingc)*$crheight]
    .bottom.rem.c configure -height \
	[expr ($cfonth+$cspacingc)*$crheight]
    .bottom.pro.c configure -height \
	[expr ($cfonth+$cspacingc)*$crheight]
    .bottom.rad.t configure -height \
	[expr ($tfonth * 2 + $cspacingt) * $crheight]
    .bottom.rem.t configure -height \
	[expr ($tfonth * 2 + $cspacingt) * $crheight]
    .bottom.pro.t configure -height \
	[expr ($tfonth * 2 + $cspacingt) * $crheight]
}


# Called to turn the English-lookup panel on or off.
proc toggleYingHan {} {
    global yhState crheight bfont pstate edTcols ssmode
    if {!$yhState} then {
	# Put up the English definition window
	if {$ssmode == "small"} {
	    # for small mode, shorten other widgets
	    incr crheight -2
	    adjustBwidgHeight
	}
	pack .verybottom -before .bottom -side bottom -fill x
	# add help msg to verybottom
	.verybottom.yinghanC delete all
	eval destroy [winfo children .verybottom.yinghanC]
	.verybottom.yinghanC create text 10 3 -anchor nw -font $bfont -text "-> Please click in the English definition panel in the center and type a word or phrase."
	set yhState 1
	.verytop.yinghan configure -relief sunken
	bind .central.defn <Return> {
	    regsub -all "_" [.central.defn get 1.0 end] "" tmp
	    dispYingHan ".verybottom.yinghanC" \
		[searchDefn [string trim $tmp] $pstate $edTcols]
	    focus .
	}
	bind .central.defn <Button-1> {
	    .central.defn delete 1.0 end }
	.central.defn configure -state normal
    } else {
	# Take down the English definition window
	pack forget .verybottom
	set yhState 0
	.verytop.yinghan configure -relief raised
	# lengthen widgets if were shortened in small mode
	if {$ssmode == "small"} {
	    incr crheight 2
	    adjustBwidgHeight
	}
	bind .central.defn <Return>
	bind .central.defn <Button-1>
	.central.defn configure -state disabled
    }
}

# The primary function for setting up all portions of the display for a
# character.  Parameter 'hflag' should be 1 to store 'cidx' in the history,
# 0 otherwise.
proc dispchar {cidx hflag} {
    # first set central display for character
    global mainchar pinyinentry mainidx flevel cstate pstate radch radmn \
	    remch rempy propy compy history hpos maxpos MAXHIST \
	    mdTcols bwTcols cwTcols miniMode
    set cinfo ""
    if { [catch { charinfo $cidx $cstate $pstate $mdTcols } cinfo ] } {
	puts stderr "hanzim: Caught TCL_ERROR from charinfo"
	genrandom
	return
    }
    set mainidx $cidx
    set mainchar [string range $cinfo 0 0]
    set cinfo [string range $cinfo 1 end]
    set pinyinentry [lindex $cinfo 6]
    set compy ""
    updatemdef [lindex $cinfo 1]
    set radch [lindex $cinfo 2]
    set radmn [lindex $cinfo 3]
    set remch [lindex $cinfo 4]
    # the following two are utf pinyin strings
    set rempy [lindex $cinfo 5]
    set propy [lindex $cinfo 6]
    # update bottom lists of parallel characters
    updatebwidg ".bottom.rad.c" ".bottom.rad.t" \
	        [sameRadList $cidx $flevel $cstate $pstate $bwTcols $miniMode]
    updatebwidg ".bottom.rem.c" ".bottom.rem.t" \
	        [sameRemList $cidx $flevel $cstate $pstate $bwTcols $miniMode]
    updatebwidg ".bottom.pro.c" ".bottom.pro.t" \
	        [samePronList $cidx $flevel $cstate $pstate $bwTcols $miniMode]
    # update left and right compound lists
    updateclist ".left.lcompc" ".left.lcompt" \
	        [LcompList $cidx $flevel $cstate $pstate $cwTcols] "left"
    updateclist ".right.rcompc" ".right.rcompt" \
	        [RcompList $cidx $flevel $cstate $pstate $cwTcols] "right"
    # history stuff
    if {$hflag && ([lindex $history $hpos] != $cidx)} then {
	if {[llength $history] < $MAXHIST} then {
	    set history [linsert $history [expr $hpos + 1] $cidx]
	    .verytop.forw configure -state disabled
	    incr hpos
	    set history [lrange $history 0 $hpos]
	    set maxpos $hpos
	} else {
	    if { $hpos == $MAXHIST } then {
		set history [lreplace $history 0 0]
		lappend history $cidx
	    } else {
		set history [linsert $history [expr $hpos + 1] $cidx]
		incr hpos
		set history [lrange $history 0 $hpos]
		set maxpos $hpos
	    }
	    .verytop.forw configure -state disabled
	}
	if {$hpos > 0} then { .verytop.back configure -state normal }
    }
    focus .
}


# Function to grab selection and attempt to display a character based on it.
# Try primary then clipboard, and if no discernible character, leave display
# as is.
proc grabSelection {} {
    global mainidx cstate mainchar
    set sel ""
    catch { selection get -selection PRIMARY -type STRING } sel
#    puts "hanzim: primary selection is \"$sel\""
    if { [string length $sel] == 0 } then {
	catch { selection get -selection CLIPBOARD -type STRING } sel
#	puts "hanzim: clipboard selection is \"$sel\""
    }
    if { [string length $sel] > 1 } then {
	scan $sel "%c%c" c1 c2
    	dispchar [convertSelection $c1 $c2 $cstate $mainidx] 1
    } else {
	puts ""
    }
}


# Function to pop up a help screen.
proc helpPopup {} {
    global ssmode bfont
    toplevel .help -relief sunken -bd 4
    wm geometry .help +100+0
    wm title .help "Help"
    bind .help <Escape> {destroy .help}
    if {$ssmode == "small"} { set hfsize "7" }
    if {$ssmode == "medium"} { set hfsize "10" }
    if {$ssmode == "large"} { set hfsize "12" }
    text .help.text -width 60 -height 38 -relief flat -bd 4 \
	    -font "helvetica $hfsize" -wrap word
    .help.text tag configure instrStyle -font "times [expr $hfsize + 4] bold"
    .help.text tag configure titleStyle -underline true \
	-font "helvetica [expr $hfsize + 2] bold"

    # Title
    .help.text insert insert "\nHanzim:  " {instrStyle}
    .help.text insert insert "Interactive Character Display and Dictionary Tool\n"

    # Initial intro
    .help.text insert insert "\nThe lone character in the middle of the screen is the \"main character\", and all other information displayed is relative to it.\n"

    # Description of lists
    .help.text insert insert "\nList" {titleStyle}
    .help.text insert insert "\t\t    "
    .help.text insert insert "Contents\n\n" {titleStyle}
    .help.text insert insert "Bottom Left\t    characters with same radical\n"
    .help.text insert insert "Bottom Middle\t    characters with same phonetic component\n"
    .help.text insert insert "Bottom Right\t    characters with same pronunciation\n"
    .help.text insert insert "Upper Left\t    compounds with main character second\n"
    .help.text insert insert "Upper Right\t    compounds with main character first\n"

    # Other description
    .help.text insert insert "\n\nThe central slider sets the minimum frequency a character must have to be displayed in any list.\n\n"
    .help.text insert insert "Look up characters by either typing pinyin in the panel just above the main character, or by clicking on any character in a list, or by selecting a radical from the stroke-number menus on top (will set main character to include that radical).\n\n"
    .help.text insert insert "To look up a character whose definition contains an English word, word-fragment, or phrase, click the \"ying-han\" button at the top right and enter the text in the central definition panel.\n\n"
    .help.text insert insert "You may also direct Hanzim to go to the first Chinese character in the current selected text (in any application) by typing \"Ctrl-g\" or \"Alt-g\".\n"
    
    # Keys
    .help.text insert insert "\nKey\t\tFunction\n\n" {titleStyle}
    .help.text insert insert "Ctrl/Alt-g\t\tGo to selection\n"
    .help.text insert insert "Ctrl/Alt-p\t\tToggle pronunciation display\n"
    .help.text insert insert "Ctrl/Alt-h\t\tHelp\n"
    .help.text insert insert "Ctrl/Alt-q/x\tQuit\n"

    # Remaining set-up
    .help.text configure -state disabled
    button .help.closeButton -text "Close" -command {destroy .help} -pady 1m \
	    -font $bfont
    pack .help.text -in .help -side top
    pack .help.closeButton -in .help -side bottom
}

# Updates the main English definition text panel.
proc updatemdef {defstr} {
    global yhState
    .central.defn configure -state normal
    .central.defn delete 1.0 end
    .central.defn insert end $defstr
    if {$yhState == 0} then { .central.defn configure -state disabled }
}

# Given canvases for character and text representations, wipe them clean then
# fill them with text objects (twidg) and text and tagged rectangle objects
# (cwidg) displaying elements in chrlist ({<index> <fontrep> <pinyin> <defn>}).
proc updatebwidg {cwidg twidg chrlist} {
    global cfont tfont cfontw cfonth tfontw tfonth cspacingc cspacingt \
	   highlight same_hz_fg same_as_fg same_hz_bg same_as_bg miniMode
    $cwidg delete all
    $twidg delete all
    set ylen [expr [llength $chrlist] * ($cfonth + $cspacingc)]
    $cwidg configure -scrollregion "0 0 100 $ylen"
    $twidg configure -scrollregion "0 0 1000 $ylen"
# create and bind text and enclosing rectangle objects for each entry
    set vloc 2
    foreach chrline $chrlist {
	set idx [lindex $chrline 0]
	# character
	set idr [$cwidg create rectangle 4 [expr $vloc - 1] \
		[expr $cfontw + 5] [expr $vloc+$cfonth-2] -outline ""]
	set idt [$cwidg create text 5 $vloc -anchor nw -fill $same_hz_fg \
		-font $cfont -text [lindex $chrline 1]]
	$cwidg bind $idt <Enter> \
		"eval $cwidg itemconfigure $idr -fill $highlight"
	$cwidg bind $idt <Leave> \
		"eval $cwidg itemconfigure $idr -fill $same_hz_bg"
	$cwidg bind $idt <Enter> "+eval set compy [lindex $chrline 2]"
	$cwidg bind $idt <Leave> {+set compy ""}
	set a "dispchar $idx 1"
	$cwidg bind $idt <Button-1> "eval $a"
	# text
	if { $miniMode } {
	    set a [lindex $chrline 4]
	} else {
	    set a [join [lrange $chrline 3 4] "\t"]
	}
	$twidg create text 3 [expr $vloc - 2] -anchor nw -fill $same_as_fg \
		-font $tfont -text $a
	incr vloc [expr $cfonth + $cspacingc]
    }
    $cwidg yview moveto 0
    $twidg yview moveto 0
}

# Analog to updatebwidg for compounds; cwidg and twidg are canvases as before,
# now chrlist is a list of lists of form {<idx1 idx2> <fontrep> <defn>}.
proc updateclist {cwidg twidg complist side} {
    global cfont tfont cfontw cfonth tfontw tfonth cspacingc cspacingt \
	   highlight comp_hz_bg comp_hz_fg comp_as_bg comp_as_fg
    $cwidg delete all
    $twidg delete all
    set ylen [expr [llength $complist] * ($cfonth + $cspacingc)]
    $cwidg configure -scrollregion "0 0 100 $ylen"
    $twidg configure -scrollregion "0 0 1000 $ylen"
# create and bind text and enclosing rectangle objects for each entry
    set vloc 2
    foreach compline $complist {
	set idx1 [lindex [lindex $compline 0] 0]
	set idx2 [lindex [lindex $compline 0] 1]
	# character
	set idr1 [$cwidg create rectangle 4 [expr $vloc - 1] \
		[expr $cfontw + 5] [expr $vloc+$cfonth-2] -outline ""]
	set idr2 [$cwidg create rectangle [expr $cfontw + 4] [expr $vloc-1] \
		[expr $cfontw * 2 + 4] [expr $vloc+$cfonth-2] -outline ""]
	set idt1 [$cwidg create text 5 $vloc -anchor nw -fill $comp_hz_fg \
		-font $cfont -text [string range [lindex $compline 1] 0 0]]
	set idt2 [$cwidg create text [expr $cfontw + 4] $vloc -anchor nw \
		-fill $comp_hz_fg -font $cfont \
		-text [string range [lindex $compline 1] 1 1]]
	$cwidg bind $idt1 <Enter> \
		"eval $cwidg itemconfigure $idr1 -fill $highlight"
	$cwidg bind $idt1 <Leave> \
		"eval $cwidg itemconfigure $idr1 -fill $comp_hz_bg"
	set a "dispchar $idx1 1"
	$cwidg bind $idt1 <Button-1> "eval $a"
	$cwidg bind $idt2 <Enter> \
		"eval $cwidg itemconfigure $idr2 -fill $highlight"
	$cwidg bind $idt2 <Leave> \
		"eval $cwidg itemconfigure $idr2 -fill $comp_hz_bg"
	set a "dispchar $idx2 1"
	$cwidg bind $idt2 <Button-1> "eval $a"
	# used to only do first two for "left", second two for "right"
	$cwidg bind $idt1 <Enter> "+eval set compy [lindex $compline 2]"
	$cwidg bind $idt1 <Leave> {+set compy ""}
	$cwidg bind $idt2 <Enter> "+eval set compy [lindex $compline 3]"
	$cwidg bind $idt2 <Leave> {+set compy ""}
	# text
	set a [lindex $compline 4]
	$twidg create text 4 [expr $vloc - 2] -anchor nw -fill $comp_as_fg \
		-font $tfont -text $a
	incr vloc [expr $cfonth + $cspacingc]
    }
    $cwidg yview moveto 0
    $twidg yview moveto 0
}

# This function works like the last two but generates widgets on the canvas
# rather than entries in a window.
proc dispYingHan {cnvwidg elist} {
    global cfont cdfont edTcols cfontw tfontw ssmode
    set idxlist1 ""
    set idxlist2 ""
    set idxlist3 ""
    set pylist1 ""
    set pylist2 ""
    set pylist3 ""
    # this is a hack which should be removed...
    set iii ""
    set idlistc ""
    set idlistt ""
    if { $ssmode == "large" } { set pWidth 2 }
    if { $ssmode == "medium" } { set pWidth 3 }
    if { $ssmode == "small" } { set pWidth 4 }
    set winWidth [expr $cfontw * 4 + $tfontw * ($edTcols+$pWidth)]
    $cnvwidg delete all
    eval destroy [winfo children $cnvwidg]
    #set xlen [expr [llength $elist] * 260 + 20]
    set xlen [expr [llength $elist] * $winWidth + 20]
    $cnvwidg configure -scrollregion "0 0 $xlen 8m"
    set i 1
    foreach eline $elist {
	lappend idxlist1 [lindex [lindex $eline 0] 0]
	lappend idxlist2 [lindex [lindex $eline 0] 1]
	lappend pylist1 [lindex $eline 2]
	lappend pylist2 [lindex $eline 3]
	frame $cnvwidg.f$i -relief ridge
	text $cnvwidg.c$i -font $cfont -width 4 -height 1 -bg #d9d9d9
	text $cnvwidg.t$i -font $cdfont -width $edTcols -height 1 -bg #d9d9d9
	if { [llength $eline] == 6 } {
	    lappend idxlist3 [lindex [lindex $eline 0] 2]
	    lappend pylist3 [lindex $eline 4]
	    $cnvwidg.c$i configure -width 6
	    $cnvwidg.t$i insert end [lindex $eline 5]
	    lappend iii $i
	    set wid 270
	} else {
	    $cnvwidg.t$i insert end [lindex $eline 4]
	    set wid 240
	}
	$cnvwidg.c$i insert end [lindex $eline 1]
	pack $cnvwidg.c$i $cnvwidg.t$i -in $cnvwidg.f$i -side left -padx 5
	$cnvwidg create window [expr ($i-1) * $winWidth + 10 + \
		([llength $iii] > 1) * ([llength $iii]-1) * 30] 1 \
		-width $wid -height 32 -window $cnvwidg.f$i -anchor nw
	incr i
    }
    $cnvwidg configure -scrollregion \
	    "0 0 [expr $xlen + [expr [llength $iii] * 30]] 8m"
    set i 1
    # bind characters to display events
    # compounds
    foreach idx1 $idxlist1 idx2 $idxlist2 py1 $pylist1 py2 $pylist2 {
	$cnvwidg.c$i tag add char1$i 1.0 1.1
	set a "dispchar $idx1 1"
	$cnvwidg.c$i tag bind char1$i <Button-1> "eval $a"
	$cnvwidg.c$i tag add char2$i 1.1 1.2
	set a "dispchar $idx2 1"
	$cnvwidg.c$i tag bind char2$i <Button-1> "eval $a"
	# highlighting
	$cnvwidg.c$i tag bind char1$i <Enter> \
		"eval $cnvwidg.c$i tag configure char1$i -background white"
	$cnvwidg.c$i tag bind char1$i <Leave> \
		"eval $cnvwidg.c$i tag configure char1$i -background #d9d9d9"
	$cnvwidg.c$i tag bind char2$i <Enter> \
		"eval $cnvwidg.c$i tag configure char2$i -background white"
	$cnvwidg.c$i tag bind char2$i <Leave> \
		"eval $cnvwidg.c$i tag configure char2$i -background #d9d9d9"
	# pin-yin display
	$cnvwidg.c$i tag bind char1$i <Enter> \
		"+eval set compy $py1"
	$cnvwidg.c$i tag bind char1$i <Leave> {+set compy ""}
	$cnvwidg.c$i tag bind char2$i <Enter> \
		"+eval set compy $py2"
	$cnvwidg.c$i tag bind char2$i <Leave> {+set compy ""}

	$cnvwidg.c$i configure -state disabled -cursor {}
	$cnvwidg.t$i configure -state disabled -cursor {}
	incr i
    }
    # triplets; this code should be consolidated with above; depends on
    # the triplets all being continuous and after the compounds
    foreach idx3 $idxlist3 py3 $pylist3 i $iii {
	$cnvwidg.c$i tag add char3$i 1.2 1.3
	set a "dispchar $idx3 1"
	$cnvwidg.c$i tag bind char3$i <Button-1> "eval $a"
	# highlighting
	$cnvwidg.c$i tag bind char3$i <Enter> \
		"eval $cnvwidg.c$i tag configure char3$i -background white"
	$cnvwidg.c$i tag bind char3$i <Leave> \
		"eval $cnvwidg.c$i tag configure char3$i -background #d9d9d9"
	# pin-yin display
	$cnvwidg.c$i tag bind char3$i <Enter> \
		"+eval set compy $py3"
	$cnvwidg.c$i tag bind char3$i <Leave> {+set compy ""}
	incr i
    }

}

# This function initializes a set of menus containing radicals with particular
# numbers of strokes; it is called with the result of the C function radList()
# -- a list of lists of radicals by stroke number, each accompanied by a char-
# acter index.
proc radInitMenu {mwidgLoc rlist} {
    global bfont cfont rmheight
    set ssnum 1
    foreach strokeSet $rlist {
	set strokeTitle [lindex $strokeSet 0]
	set strokeSet [lreplace $strokeSet 0 0]
	set ssl [llength $strokeSet]

	# initialize menubutton
	menubutton $mwidgLoc.rmenu$ssnum -text $strokeTitle \
	    -font $bfont -menu $mwidgLoc.rmenu$ssnum.list
	menu $mwidgLoc.rmenu$ssnum.list -font $cfont -tearoff false
	set sspos 0
	foreach rad $strokeSet {
	    # add entry and binding
	    set a "dispchar [lindex $rad 0] 1"
	    if { ([expr $sspos % $rmheight] == 0) && ($sspos != 0)  } {
		set newColumn 1
	    } else {
		set newColumn 0
	    }
	    $mwidgLoc.rmenu$ssnum.list add command -columnbreak $newColumn \
		-label [lreplace $rad 0 0] -command "eval $a"
	    incr sspos
	}
 	pack $mwidgLoc.rmenu$ssnum -in $mwidgLoc -side left
	incr ssnum
    }
}


# This function sends yview commands to two widgets given as arguments
# we used to scroll the second twice as much as the first by pulling out the
# numeric argument and doubling it, but the new Tk syntax goes by percentages
# so we can't do this - arrows don't work properly :(
proc scroll2 {tw1 tw2 args} {
    eval $tw1 yview $args
    eval $tw2 yview $args
#    eval $tw2 yview [lindex $args 0] [expr [lindex $args 1] * 2] \
#	    [lindex $args 2]
}

# This function sends a scrollbar update command to a scrollbar and a
# yview command to its partner widget.  It is not currently used...
proc scroll2other {tw sw n1 n2} {
    $sw set $n1 $n2
    if {[lindex [$tw yview] 0] != $n1} {
	$tw yview moveto $n1
    }
}

# Utility function for debugging purposes.
proc redo { } {
    eval destroy [winfo children .]
    set argc 0
    set argv ""
    set fontPropsFile "/home/arobert/Hanzim/Data/fonts.unix"
    source /home/arobert/Hanzim/Data/hanwin.tcl
}


##############################################################################
############################  PACKING  #######################################
##############################################################################

# actually set up the window
wm geometry . +2+0
#wm resizeable . 0 0

pack .verytop.back -in .verytop -side left -anchor w
pack .verytop.forw -in .verytop -side left -anchor w

# initialize radical menu
radInitMenu ".verytop.menus" [radList $cstate]
pack .verytop.menus -in .verytop -side left -padx $vtpadx
pack .verytop.yinghan -in .verytop -side right
pack .verytop -side top -fill x

# scrolling lists, bottom half
pack .bottom.rad.m.ch .bottom.rad.m.mn -in .bottom.rad.m \
	-side left -anchor w -padx 4
pack .bottom.rad.m -in .bottom.rad
pack .bottom.rad.c -in .bottom.rad -side left -anchor sw
pack .bottom.rad.scrollsrad -in .bottom.rad -side left -fill y
pack .bottom.rad.t -in .bottom.rad -side left

pack .bottom.rem.m.ch .bottom.rem.m.mn -in .bottom.rem.m \
	-side left -anchor w -padx 4
pack .bottom.rem.m -in .bottom.rem
pack .bottom.rem.c -in .bottom.rem -side left
pack .bottom.rem.scrollsrem -in .bottom.rem -side left -fill y
pack .bottom.rem.t -in .bottom.rem -side left

pack .bottom.pro.m.ch .bottom.pro.m.mn -in .bottom.pro.m \
	-side left -anchor w -padx 4
pack .bottom.pro.m -in .bottom.pro
pack .bottom.pro.c -in .bottom.pro -side left
pack .bottom.pro.scrollspro -in .bottom.pro -side left -fill y
pack .bottom.pro.t -in .bottom.pro -side left

pack .bottom.rad -in .bottom -side left
pack .bottom.spacer1 -in .bottom -fill x -side left
pack .bottom.rem -in .bottom -side left
pack .bottom.spacer2 -in .bottom -fill x -side left
pack .bottom.pro -in .bottom -side left
pack .bottom -side bottom -fill x

# scrolling lists, compounds
pack .left.lcompt -in .left -side left
pack .left.scrolllcomp -in .left -side left -fill y
pack .left.lcompc -in .left -side left
pack .left -side left
pack .right.rcompt -in .right -side right
pack .right.scrollrcomp -in .right -side right -fill y
pack .right.rcompc -in .right -side right
pack .right -side right

# central area
pack .central.buttons.simp -in .central.buttons -side top
pack .central.buttons.trad -in .central.buttons -side top
pack .central.buttons.random -in .central.buttons -side left
pack .central.buttons.quit -in .central.buttons -side right
pack .central.buttons -in .central -side top
pack .central.fscale -in .central -side top
pack .central.pyenter -in .central -side top
pack .central.char -in .central -side top
pack .central.defn -in .central -side top
pack .central.compy -in .central -side bottom -expand 1 -fill y
pack .central -expand 1 -fill y

# Switch to mini mode if requested
if { $hidePron } { toggleMiniMode }
