#!/bin/sh
# the next line restarts using wish \
exec wish8.0 "$0" "$@"

# This is "ding", 
#  * a dictionary lookup program,
#  * DIctionary Nice Grep,
#  * a Front-End to [ae]grep,
#  * Ding {n} :: thing
# Copyright (c) Frank Richter <Frank.Richter@hrz.tu-chemnitz.de> 1999
# GNU public license
# Ding comes with ABSOLUTELY NO WARRANTY.
# 

# To do: results as table

# Config options you may want to change:

# The provided German-English dictionary:
set default_dictfile /usr/dict/ger-eng.txt

set default_separator " :: "
set default_language1  "Deutsch"
set default_language2  "English"

# check for these "grep" commands
set grepcmds {agrep egrep}
# now searching the grep command later
# set default_grepcmd agrep
set default_grepopts "-h"

set default_minlength 3
set default_maxlength 30
set default_maxresults 200
set default_maxhistory 50
set default_bcolor [. cget -background]
set default_fcolor black
set default_shapedresult 1

set balloonBackground   LightGoldenrodYellow
set default_balloonDelay        800
set default_showBalloons        1
array set balloonHelp   {}

set lfont "-*-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*"
set bfont "-*-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
set sfont "-*-Helvetica-Medium-R-Normal--*-100-*-*-*-*-*-*"
# set default_tfont "-*-fixed-medium-r-normal--14-*-*-*-*-*-*-*"
set default_tfont {Fixed 13}
# set default_tfont "-*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*"

# set lfont {Helvetica 12 normal}
# set bfont {Helvetica 12 bold}
# set sfont {Helvetica 10}
# set default_tfont {Fixed 12}

set tfont $default_tfont
set tfonts {Courier Fixed Lucidatypewriter Helvetica Terminal}


### don't change from here unless you know what you're doing .-)
# set defaults
set dictfile $default_dictfile
set separator $default_separator
set language1     $default_language1
set language2     $default_language2
set minlength $default_minlength
set maxlength $default_maxlength
set maxresults $default_maxresults
set maxhistory $default_maxhistory
set showBalloons    $default_showBalloons
set balloonDelay     $default_balloonDelay
set bcolor $default_bcolor
set fcolor $default_fcolor
set shapedresult $default_shapedresult

set curhistory 0
set inshistory 0
array set history_result {}
array set history_query {}
set dictfiles [glob -nocomplain $dictfile]

set pname "Ding: Dictionary Lookup"
set version "1.0"
set authormail "Frank.Richter@hrz.tu-chemnitz.de"
set author "Frank Richter <$authormail>"

# language settings
set languages(en) "English"
set languages(de) "Deutsch"
set default_language "en"
set lang $default_language

# English
set s(en)(file) "File"
set s(en)(quit) "Quit"
set s(en)(save) "Save..."
set s(en)(saveall) "Save all..."
set s(en)(mail) "Send corrections..."
set s(en)(options) "Options"
set s(en)(soptions) "Search options..."
set s(en)(general) "General preferences..."
set s(en)(saveopts) "Save options"
set s(en)(help) "Help"
set s(en)(about) "About..."
set s(en)(noback) "No previous search results."
set s(en)(noforw) "No more search results."
set s(en)(query) "Search word:"
set s(en)(search) "Search"
set s(en)(clear) "Clear"
set s(en)(fullw) "full words"
set s(en)(partial) "partial search"
set s(en)(igncase) "ignore case"
set s(en)(exactcase) "exact case"
set s(en)(errors) "errors"
set s(en)(error) "error"
set s(en)(closestmatch) "best match"
set s(en)(simple) "simple search"
set s(en)(regex) "reg. expression"
set s(en)(cmd) "Search with"
set s(en)(bbhistory) "Go to previous results"
set s(en)(bfhistory) "Go to next results"
set s(en)(bwords) "Search for full words\nor partial matches?"
set s(en)(bcase) "Search case insensitive or sensitive?"
set s(en)(berrors) "Try error correction?"
set s(en)(bregex) "Simple patterns with *\nor regular expressions?"
set s(en)(color) "Select color"
set s(en)(results) "results"
set s(en)(result) "result"
set s(en)(noresults) "No results for"
set s(en)(osearcho) "Set options"
set s(en)(apply) "Apply"
set s(en)(default) "Default"
set s(en)(cancel) "Cancel"
set s(en)(dictfile) "Dictionary file"
set s(en)(nodictfile) "No Dictionary file found! See Options | Search options ..."
set s(en)(lang) "Language"
set s(en)(sep) "Separator"
set s(en)(maxresults) "Max. number of results"
set s(en)(minlength) "Min. length of search word"
set s(en)(maxlength) "Max. length of search word"
set s(en)(maxhistory) "Remember how many results"
set s(en)(fg) "Foreground color..."
set s(en)(bg) "Background color..."
set s(en)(balloon) "Show balloon help"
set s(en)(after) "after"
set s(en)(ms) "msec"
set s(en)(mailtitle) "Send e-mail to"
set s(en)(send) "Send e-mail"
set s(en)(notext) "No text for your e-mail?\nNo message sent."
set s(en)(nomail) "Please send your bug report with a mail program."
set s(en)(tooshort) "Search word too short!"
set s(en)(toolong) "Search word too long!"
set s(en)(more) "(found more, max limit reached)"
set s(en)(tfont) "Result font"
set s(en)(shaped) "Results shaped"
set s(en)(grepcmd) "Search command"
set s(en)(grepopts) "Options"
set s(en)(noagrep) "The \"agrep\" command wasn't found on your system.
Some functions won't be available for searching.\n
As a recommendation - install agrep.
You'll find it in a special agrep package or within the Glimpse package.\n
We will use for now: "

# German
set s(de)(file) "Datei"
set s(de)(quit) "Beenden"
set s(de)(save) "Speichern..."
set s(de)(saveall) "Alles speichern..."
set s(de)(mail) "Korrektur senden..."
set s(de)(options) "Einstellungen"
set s(de)(soptions) "Suchoptionen..."
set s(de)(general) "Allgemein..."
set s(de)(saveopts) "Einstellungen speichern"
set s(de)(help) "Hilfe"
set s(de)(about) "Info..."
set s(de)(noback) "Keine frheren Suchergebnisse."
set s(de)(noforw) "Keine weiteren Suchergebnisse."
set s(de)(query) "Suchwort:"
set s(de)(search) "Suche"
set s(de)(clear) "Lschen"
set s(de)(fullw) "ganze Wrter"
set s(de)(partial) "Teilsuche"
set s(de)(igncase) "Gro/klein egal"
set s(de)(exactcase) "Gro/klein exakt"
set s(de)(errors) "Fehler"
set s(de)(error) "Fehler"
set s(de)(closestmatch) "bis Treffer"
set s(de)(simple) "einfache Suche"
set s(de)(regex) "reg. Ausdrcke"
set s(de)(cmd) "Suche mit"
set s(de)(bbhistory) "Zeigt frhere Suchergebnisse"
set s(de)(bfhistory) "Zeigt weitere Suchergebnisse"
set s(de)(bwords) "Suche nach vollstndigen Wrtern\noder Muster in Wrtern?"
set s(de)(bcase) "Unterscheidung Gro-/Kleinschreibweise?"
set s(de)(berrors) "Versuche Fehlerkorrektur?"
set s(de)(bregex) "Einfache Muster mit * oder\nkomplexe regulre Ausdrcke?"
set s(de)(color) "Farbauswahl"
set s(de)(results) "Ergebnisse"
set s(de)(result) "Ergebnis"
set s(de)(noresults) "Kein Ergebnis fr"
set s(de)(osearcho) "Suchoptionen einstellen"
set s(de)(apply) "bernehmen"
set s(de)(default) "Standard"
set s(de)(cancel) "Abbrechen"
set s(de)(dictfile) "Wrterbuch-Datei"
set s(de)(nodictfile) "Keine Wrterbuch-Datei gefunden! Siehe Einstellungen | Suchoptionen"
set s(de)(lang) "Sprache"
set s(de)(sep) "Trennzeichen"
set s(de)(maxresults) "Maximalanzahl von Ergebnissen"
set s(de)(minlength) "Minimale Lnge des Suchwortes"
set s(de)(maxlength) "Maximale Lnge des Suchwortes"
set s(de)(maxhistory) "Merken wievieler Ergebnisse"
set s(de)(fg) "Vordergrundfarbe..."
set s(de)(bg) "Hintergrundfarbe..."
set s(de)(balloon) "Hilfen anzeigen"
set s(de)(after) "nach"
set s(de)(ms) "ms"
set s(de)(mailtitle) "Sende E-Mail an"
set s(de)(send) "E-Mail absenden"
set s(de)(notext) "Kein Text in der E-Mail?\nKeine Nachricht gesendet."
set s(de)(nomail) "Bitte senden Sie Ihre E-Mail mit einem Mail-Programm."
set s(de)(tooshort) "Suchbegriff zu kurz!"
set s(de)(toolong) "Suchbegriff zu lang!"
set s(de)(more) "(weitere vorhanden, Suche abgebrochen)"
set s(de)(tfont) "Schriftart fr Resultat"
set s(de)(shaped) "Resultate farblich abgesetzt"
set s(de)(grepcmd) "Such-Kommando"
set s(de)(grepopts) "Optionen"
set s(de)(noagrep) "Das Kommando \"agrep\" wurde in Ihrem System nicht gefunden.
Dadurch stehen einige Funktionen nicht zur Verfgung.\n
Empfehlung: agrep installieren!
Sie finden es in einem speziellen agrep-Paket oder im glimpse-Paket.\n
Jetzt wird verwendet: "


# if Shell variable LANG set AND we've defined this language, 
# set this as default
if [info exists env(LANG)] {
    foreach l [array names languages] {
        if [string match "$l*" $env(LANG)] {
            set default_language $l
            set lang $l
            break
        }
    }
}

# read user's config file if existing
if [file readable $env(HOME)/.dingrc] {
    set err [catch "source $env(HOME)/.dingrc" errmsg]
    if $err {
        puts stderr "Error in config file $env(HOME)/.dingrc:\n$errmsg"
        exit
    }
}

tk_setPalette foreground $fcolor background $bcolor

############
# check available grep command
set grepcmd {}
foreach c $grepcmds {
    foreach p [split $env(PATH) :] {
        if [file executable "$p/$c"] {
            # puts "Using $p/$c"
            set grepcmd $c
            set default_grepcmd $c
            break
        }
    }
    if [string compare $grepcmd ""] {
        break
    }
}
if ![string compare $grepcmd ""] {
    puts "No grep commands like $grepcmds found ... exiting"
    puts "Check your PATH and/or install a grep command"
    exit
}

set grepopts $default_grepopts

#########################################################################
# Balloon help, by John Haxby <jch@pwd.hp.com>, with slight changes
# by Axel Boldt <boldt@math.ucsb.edu>.
#

proc BalloonInit {} {
    global balloonDelay

    
bind balloon <Enter> {
    if { [info exists balloonHelp(%W)] && [%W cget -state] != "disabled"} {
        set balloonHelp(%W,after) [after $balloonDelay {showBalloonHelp %W}]
    }
}   

bind balloon <Leave> {
    unShowBalloonHelp %W
}

bind balloon <Any-KeyPress> {
    unShowBalloonHelp %W
}

bind balloon <Any-Button> {
    unShowBalloonHelp %W
}
proc showBalloonHelp {w} {
    global balloonHelp showBalloons balloonBackground lfont
    if {![info exists balloonHelp($w)] || ! $showBalloons } {
        return
    }
    update idletasks
    set curpos [winfo pointerxy $w]
    set curwin [eval winfo containing $curpos]
    if { $w == $curwin } {
        if ![winfo exists .balloon] {
            toplevel .balloon
            wm overrideredirect .balloon true
            pack [label .balloon.l -font $lfont \
                    -foreground black \
                    -background $balloonBackground \
                    -highlightthickness 1 \
                    -highlightbackground black]
            wm withdraw .balloon
        }
        .balloon.l configure -text $balloonHelp($w)
        set x [expr [lindex $curpos 0]-14]
        set y [expr [lindex $curpos 1]+19]
        wm geometry .balloon +$x+$y
        # This update is important to have the geometry command take 
        # effect in all cases (A.B.)
        update idletasks
        raise .balloon
        wm deiconify .balloon
    }
}
proc unShowBalloonHelp {w} {
    global balloonHelp
    if [info exists balloonHelp($w,after)] {
        after cancel $balloonHelp($w,after)
        unset balloonHelp($w,after)
    }
    catch {wm withdraw .balloon}
}

# end of proc BalloonInit
} 

BalloonInit
###############

proc shadeColor {color} {
    set dc 15
    scan $color "#%2x%2x%2x" red green blue
    # puts "$red $green $blue"
    set red [expr $red < (255 - $dc) ? [expr $red + $dc] : [expr $red - $dc]]
    set green [expr $green < (255 - $dc) ? [expr $green + $dc] : [expr $green - $dc]]
    set blue [expr $blue < (255 - $dc) ? [expr $blue + $dc] : [expr $blue - $dc]]
    # puts "$red $green $blue"
    return [format "#%02x%02x%02x" $red $green $blue]
}

###############

wm title . $pname
wm iconname . $pname

menu .menuBar -font $lfont -tearoff 0 -relief groove
.menuBar add cascade -menu .menuBar.file -label [set s($lang)(file)] -underline 0
menu .menuBar.file -tearoff 0  -font $lfont
.menuBar.file add command -label [set s($lang)(save)] -command {save 0}  \
    -underline 0 -accelerator "Ctl+S"
.menuBar.file add command -label [set s($lang)(saveall)] -command {save 1} \
    -underline 0 -accelerator "Ctl+L"
.menuBar.file add command -label [set s($lang)(mail)] -command sendMail \
    -underline 0 -accelerator "Ctl+M"
.menuBar.file add command -label [set s($lang)(quit)] -command "exit" \
    -underline 0 -accelerator "Ctl+Q"
. configure -menu .menuBar

.menuBar add cascade -menu .menuBar.opts -label [set s($lang)(options)] -underline 0
menu .menuBar.opts -tearoff 0  -font $lfont
.menuBar.opts add command -label [set s($lang)(general)] -command "setGeneral" -underline 0 
.menuBar.opts add command -label [set s($lang)(soptions)] -command "setOptions" -underline 0 
.menuBar.opts add command -label [set s($lang)(saveopts)] -command "saveOptions" -underline 0 

.menuBar add cascade -menu .menuBar.help -label  [set s($lang)(help)] -underline 0
menu .menuBar.help -tearoff 0  -font $lfont
.menuBar.help add command -label  [set s($lang)(help)] -command "helpBox" \
    -underline 0 -accelerator "F1"
.menuBar.help add sep
.menuBar.help add command -label  [set s($lang)(about)] -command "aboutBox" \
    -underline 0

bind . <F1> helpBox
bind . <Control-s> {save 0}
bind . <Control-l> {save 1}
bind . <Control-q> exit
bind . <Control-m> sendMail

frame .search 
pack .search -side top -fill x

frame .search.l
pack .search.l -side right -fill y -pady 4 -padx 4
image create photo logo1 -data {
R0lGODdhIAAgAMIAAMDAwAAAAP///4CAgPJ8Dv///////////ywAAAAAIAAgAAADxRi63P5N
yEmrvVIJwLv/YDhoYWl6Y7CdLJiubQy8ALR0tsLRKiYENd9vpyH0MDWAMECYFY+X5LL5Mv4C
g4EgK5Eustwf9SmU+phOlVWiBXeBE7dWnBaskfDzWA21mDFoVX0VXlhbYYFkV1hhfxeJfGV5
gHt2UHMTZmCYdIJxh3OOFpCWkkqHGQyVa5sUokFXq4Ouk29DpHdRtRs6NbI2vEo5siZAxsXE
xb0luDlMBEbDddDU1dRg1tmVMjIpm9/g4eLO5MAJADs=}
image create photo logo2 -data {
R0lGODdhIAAgAMIAAMDAwAAAAP///4CAgPJ8Dv///////////ywAAAAAIAAgAAAD2Bi63P5N
yEmrvVIJwLv/nwIOGmiCgRCM5emmKpu6Jxx/5AwtnTY1nNzGdlkRB6pfUEMgWgCrCTKZIQCE
TYwECpBMqSorNqUYIM3bKAwZmAbETGeF67S9r3GpwDzl6sF3Y1pDXXs/bnApWVp+FCQ/iQKL
GHR6dpGLC3xphRRtVXiKchSNbpehkkdzUW6AmDZnaIQwOmiBeYxRVLphqIt8X4RKvLeig40q
vL2CuYUiFcWpO8I70U0vdC+vNDzavtVvBE3VmOLm5+J86OuRNO4mOcDy8/T14PfTCQA7}
image create photo logo3 -data {
R0lGODdhIAAgAMIAAMDAwAAAAP///4CAgPJ8Dv///////////ywAAAAAIAAgAAADzxi63P5N
yEmrvVIJwLv/QAB+gzaeytmVwaZ6rfiy7htmNh1CactCHF0LIxgWMTIawXjxHZsEgJJpCQwy
1Ex0WrReB9fnTwAOB7aaJfF4zWqlaeMXnJloquiWmuj0zt9cfGZIeQJ7SHaEcHpDC2V1XV2P
RYWHTUdulIuGTGF2MTd4m4dznkVBdGRtlZkZJQAVnmejQ2WmKRKpFLOBSAOwfKxrVsCKUzwb
IjwKrCrKryjNzinOrMsE2Mu8adjd3thl3+KFNuUkCrbp6uvs2u7ICQA7}
image create photo logo4 -data {
R0lGODdhIAAgAMIAAMDAwAAAAP///4CAgPJ8Dv///////////ywAAAAAIAAgAAAD2xi63P5N
yEmrvVIJwDsPXih6gzYCyqkCZbCNLriK7Su6gjx3NfpkHciC08NNUBOQ8SKrEZYbDRCTIbA0
zwoSB8owrc5YYDBAJgE5DXlQvbqyFDO7Sw2AsUZ2TVBCSyt2bgJwRzFTfGSIbWFaOHNodXdv
UD5nVDmSg1wphjl+aWuLeIBJnpeBjBQubFN/qpmEJROsdKGKqKNJtaYVrJiCcGuVj4i+SbCU
H4cYuJMXynTMyEJKfkLIKkM6N9jZKdnT1wRP1LDj5+jja+nsmTvvPApr8/T19vTU+fkCCQA7}
image create photo logo5 -data {
R0lGODdhIAAgAMIAAMDAwAAAAP///4CAgPJ8Dv///////////ywAAAAAIAAgAAAD3Ri63P5N
yEmrvVIJwLnqYCh2gxYGQjCuYYmeGSu7G4jG8kgD0fR5kN/uxktRVDeMCkAjEG9JHgCTITA1
TkmxIs1cAtYmCikYSMzdRXlgToWxT1R70KWCr6jsZkwpTTUWd2JaUBN1bGZsVXgCeltlhFN2
b3lPRpB1SpSNUIWRRgqIi4N7Pp+TjI5yFGlJR5uqZ4YqZ4prbqlJSW2ZE224pHSue1OiFYJw
rBpotKikhlu9gbBBL7TV1CxIUivIlTIL4NTYBE7YqeXp6uWI6+6bOfEtoYj19vf49dX7+wIJ
ADs=}

button .search.l.logo -image logo1 -bd 0 -relief flat -command aboutBox
pack .search.l.logo -side right -expand 1 -anchor n

set logoanim 1
proc animlogo {t} {
    global logoanim
    # puts "animlogo $logoanim"
    if ![winfo exists $t] {
        after cancel animlogo $t
        return
    }
    if {$logoanim >= 5} {
        set logoanim 1
    } else {
        incr logoanim
    }
    $t configure -image "logo$logoanim"
    after 100 "animlogo $t"
}

frame .search.s
pack .search.s -side top -expand 1
button .search.s.back  -state disabled -text "<" -command { history back }
set balloonHelp(.search.s.back) [set s($lang)(bbhistory)]
bindtags .search.s.back [list balloon .search.s.back Button all]

button .search.s.forw  -text ">" -state disabled -command { history forward }
set balloonHelp(.search.s.forw) [set s($lang)(bfhistory)]
bindtags .search.s.forw [list balloon .search.s.forw Button all]

label .search.s.label -text "  [set s($lang)(query)]"
entry .search.s.entry -width 24 -selectbackground blue -selectforeground white \
    -textvariable query -relief sunken -background white -font $bfont
button .search.s.button -text [set s($lang)(search)] -command "dictsearch \$query"
button .search.s.clear -text [set s($lang)(clear)] -font $lfont -command {set query ""}

pack .search.s.back .search.s.forw .search.s.label .search.s.entry -side left
pack .search.s.button -side left -pady 5 -padx 5
pack .search.s.clear -side left -pady 5
focus .search.s.entry
bind .search.s.entry <Return> "dictsearch \$query"

# Options
frame .search.opts
pack .search.opts -side top -pady 2
menubutton .search.opts.optword -textvariable wlabel -menu .search.opts.optword.menu \
    -indicatoron 1 -relief raised -anchor c -direction flush -font $lfont \
    -width 11
set balloonHelp(.search.opts.optword) [set s($lang)(bwords)]
bindtags .search.opts.optword [list balloon .search.opts.optword Menubutton all]
menu .search.opts.optword.menu -font $lfont -tearoff 0
.search.opts.optword.menu add command -label [set s($lang)(fullw)] -font $lfont -command \
    {set opts(word) 0; set wlabel [set s($lang)(fullw)]}
.search.opts.optword.menu add command -label [set s($lang)(partial)] -font $lfont -command \
    {set opts(word) 1; set wlabel [set s($lang)(partial)]}
set wlabel [set s($lang)(fullw)]
set opts(word) 0

menubutton .search.opts.optcase -textvariable clabel -menu .search.opts.optcase.menu \
    -indicatoron 1 -relief raised -anchor c -direction flush -font $lfont \
    -width 13
set balloonHelp(.search.opts.optcase) [set s($lang)(bcase)]
bindtags .search.opts.optcase [list balloon .search.opts.optcase Menubutton all]

menu .search.opts.optcase.menu -font $lfont -tearoff 0
.search.opts.optcase.menu add command -label [set s($lang)(igncase)] -font $lfont \
     -command {set opts(case) 0; set clabel [set s($lang)(igncase)]}
.search.opts.optcase.menu add command -label [set s($lang)(exactcase)] -font $lfont \
    -command {set opts(case) 1; set clabel [set s($lang)(exactcase)]}
set clabel [set s($lang)(igncase)]
set opts(case) 0

menubutton .search.opts.errors -textvariable elabel -menu .search.opts.errors.menu \
    -indicatoron 1 -relief raised -anchor c -direction flush -font $lfont \
    -width 9
set balloonHelp(.search.opts.errors) [set s($lang)(berrors)]
bindtags .search.opts.errors [list balloon .search.opts.errors Menubutton all]
menu .search.opts.errors.menu -font $lfont -tearoff 0
set opts(errors) 0
set elabel "0 [set s($lang)(errors)]"
for {set x 0} {$x < 5} {incr x} {
    set e [expr $x == 1 ? {[set s($lang)(error)]} : {[set s($lang)(errors)]}]
    .search.opts.errors.menu add command -label "$x $e" -font $lfont \
        -command "
            set opts(errors) $x; set elabel \"$x $e\""
}
.search.opts.errors.menu add command -label [set s($lang)(closestmatch)] -font $lfont \
    -command {set opts(errors) -1; set elabel [set s($lang)(closestmatch)]}

if [string compare $grepcmd "agrep"] {
    # only agrep has error correction
    .search.opts.errors configure -state disabled
}

menubutton .search.opts.regex -textvariable rlabel -menu .search.opts.regex.menu \
    -indicatoron 1 -relief raised -anchor c -direction flush -font $lfont \
    -width 13
set balloonHelp(.search.opts.regex) [set s($lang)(bregex)]
bindtags .search.opts.regex [list balloon .search.opts.regex Menubutton all]
menu .search.opts.regex.menu -font $lfont -tearoff 0
.search.opts.regex.menu add command -label [set s($lang)(simple)] -font $lfont -command \
    {set opts(regex) 0; set rlabel [set s($lang)(simple)]}
.search.opts.regex.menu add command -label [set s($lang)(regex)] -font $lfont -command \
    {set opts(regex) 1; set rlabel [set s($lang)(regex)]}
set rlabel [set s($lang)(simple)]
set opts(regex) 0

pack .search.opts.optword .search.opts.optcase .search.opts.errors .search.opts.regex -side left -fill x -padx 2


####### Status bar
frame .statusBar
pack .statusBar -side bottom -fill x -pady 2
label .statusBar.lab -text "[set s($lang)(cmd)] $grepcmd" -width 38 -relief sunken \
    -bd 1 -height 1 -font $sfont -anchor w
label .statusBar.file -relief sunken -bd 1 -height 1 \
    -font $sfont -anchor w -justify right -textvariable dictfile
pack .statusBar.lab -side left -padx 2 -expand yes -fill x
pack .statusBar.file -side left -padx 2

####### Results
frame .result
pack .result -side top -fill both -expand yes
text .result.text -setgrid 1 -state disabled -wrap none -width 76 -relief groove \
    -yscrollcommand ".result.yscroll set" -xscrollcommand ".result.xscroll set" \
    -height 16 -padx 4 -pady 4 -font $tfont
bindtags .result.text {Text . all dlookup}


scrollbar .result.yscroll -orient vertical -width 10 -command {
    .result.text yview
}
scrollbar .result.xscroll -command ".result.text xview" -orient horizontal \
    -width 10

grid .result.text -in .result -row 0 -column 0 -rowspan 1 -sticky news -padx 0
grid .result.yscroll  -in .result -row 0 -column 1 -sticky news
grid .result.xscroll  -in .result -row 1 -column 0 -sticky news
grid rowconfig    .result 0 -weight 1 -minsize 0
grid columnconfig .result 0 -weight 1 -minsize 0

# These key bindings look a bit strange: scroll result's text with key bindings
# to the entry. But the entry always has the focus...

bind .search.s.entry <Up>         [list .result.text yview scroll -1 units]
bind .search.s.entry <Down>       [list .result.text yview scroll 1 units]
bind .search.s.entry <Shift-Up>   [list .result.text yview scroll -5 units]
bind .search.s.entry <Shift-Down> [list .result.text yview scroll 5 units]
bind .search.s.entry <Prior>      [list .result.text yview scroll -1 pages]
bind .search.s.entry <Next>       [list .result.text yview scroll 1 pages]
bind .search.s.entry <Home>       [list .result.text yview moveto 0]
bind .search.s.entry <End>        [list .result.text yview moveto 1.0]
bind .search.s.entry <Shift-Left>  [list .result.text xview scroll -5 units]
bind .search.s.entry <Shift-Right> [list .result.text xview scroll 5 units]

bind .search.s.entry <Control-Up>   [list history back]
bind .search.s.entry <Control-Down> [list history forward]

if {[winfo depth .] > 1} {      # Color display
    .result.text tag configure bg1 -background [shadeColor $bcolor]
    .result.text tag configure matchfg -foreground blue
    .result.text tag configure u -background LightGoldenrodYellow
    set errcolor red
    set ncolor black
} else {
    .result.text tag configure bg1 -background white -foreground black
    .result.text tag configure matchfg -underline 1
    .result.text tag configure u -underline 1
    set errcolor black
    set ncolor black
}

if [string compare $grepcmd "agrep"] {
    .result.text configure -state normal
    .result.text insert end [set s($lang)(noagrep)]
    .result.text insert end $grepcmd
    .result.text configure -state disabled
}



#####################################
proc dictsearch {query} {
    global result dictfile dictfiles opts errcolor ncolor minlength maxlength 
    global maxhistory curhistory inshistory history_result history_query
    global grepcmd grepopts s lang logoanim

    if {[string length $dictfiles] <= 0} {
        .statusBar.lab config -foreground $errcolor -text [set s($lang)(nodictfile)]
        return
    }

    # remove shell meta chars
    set squery $query
    regsub -all {([]\[\{\} `&$])} $squery {\\\1} squery

    if {[string length $squery] < $minlength} {
        .statusBar.lab config -foreground $errcolor -text [set s($lang)(tooshort)]
        return
    }
    if {[string length $squery] > $maxlength} {
        .statusBar.lab config -foreground $errcolor -text [set s($lang)(toolong)]
        return
    }
    if ![string compare $grepcmd "agrep"] {             # search with agrep
        if {$opts(regex) == 0} {
            # prepare simple pattern for agrep
            # simple pattern
            regsub -all {\*} $squery "#" squery
            # AND
            regsub -all { *\+ *} $squery ";" squery
        } elseif [regexp {[*?.|]} $squery] {
            # reg expression - switch off word searching
            set opts(word) 1
        }
    }
    # Optionen auswerten
    set opt $grepopts
    if {$opts(word) == 0 && ![regexp { } $squery]} {
        # words only search - makes no sense if search string contains
        # white space
        set opt "$opt -w"
    }
    if {$opts(case) == 0} {
        set opt "$opt -i"
    }
    if ($opts(errors)) {
        if {$opts(errors) == -1} {
            set opt "$opt -B -y"
        } else {
            set opt "$opt -$opts(errors)"
        }
    }
    if {$opts(regex) == 0 && ![string compare $grepcmd "agrep"]} {
        # prepare simple pattern for agrep
        regsub -all {\*} $squery "#" squery
    }
    
    .statusBar.lab config -foreground $ncolor -text "$grepcmd $opt -e $squery $dictfile"
    # puts "$grepcmd $opt -e $squery $dictfile"

    set oldcursor [. cget -cursor]
    . configure -cursor watch
    .result.text configure -cursor watch
    .search.s.entry configure -cursor watch
    .search.s.entry selection range 0 [string length $query]
    update
    
    # Suche!
    set res ""
    set logoanim 1
    animlogo .search.l.logo

#if [catch {set res [eval exec $grepcmd $opt -e \"$squery\" $dictfiles]} err] 

    if [catch {set in [open [concat "|$grepcmd $opt -e $squery $dictfiles"] r]} err] {

        if [string match "child process exited abnormally" $err] {
            .statusBar.lab config -foreground $errcolor -text "[set s($lang)(noresults)] $query"
        } else {
            .statusBar.lab config -foreground $errcolor -text $err
        }
    } else {
        update idletasks
        while {[gets $in line] > -1} {
            update
            if [string length $res] {
                set res "$res\n$line"
            } else { 
                set res $line
            }
        }
        if [string length $res] {
            display $res $query
            if {$inshistory >= $maxhistory} {
                set inshistory 1
            } else {
                incr inshistory
            }
            .search.s.forw configure -state disabled
            if {$curhistory != 0} {
                .search.s.back configure -state normal
            }
            set curhistory $inshistory
            set history_query($inshistory) $query
            set history_result($inshistory) $res
        } else {
            .statusBar.lab config -foreground $errcolor -text \
                "[set s($lang)(noresults)] $query"
        }

    }

    catch {close $in}
    after 400 {
        after cancel animlogo .search.l.logo
        .search.l.logo configure -image "logo1"
    }

    . configure -cursor $oldcursor
    .result.text configure -cursor $oldcursor
    .search.s.entry configure -cursor $oldcursor
}

proc display {res query} {
    global maxresults ncolor separator language1 language2 s lang shapedresult
    set count 0
    set more ""

    .search.s.entry delete 0 end
    .search.s.entry insert 0 $query
    .search.s.entry selection range 0 [string length $query]

    set t .result.text
    $t configure -state normal
    $t delete 0.0 end

    # current text width - a bit complicated :-(
    set w [expr round(([winfo width $t] + 0.0) / [winfo reqwidth $t] * \
            ([$t cget -width] - 2) / 2)]
    # puts $w
    if {[string compare $language1 ""] || [string compare $language2 ""]} {
        $t insert end [format "%-*s  %-*s\n" $w $language1 $w $language2] u
    }

    foreach line [split $res "\n"] {
        if {$count >= $maxresults} {
            set more [set s($lang)(more)]
            break
        }
        incr count 
        if {[string compare $separator ""] && [regexp "$separator" $line]} {
            regsub "$separator.*" $line "" lang1
            regsub ".*$separator" $line "" lang2
        } else {
            set lang1 $line
            set lang2 ""
        }
        
        # regsub ".\{40\}" $lang1
        set length1 [string length $lang1]
        set length2 [string length $lang2]
        set l1 [format "%-*s"  $w $lang1]
        set l2 [format "%s" $lang2]
        if {$shapedresult && [expr $count % 2]} {
            $t insert end "$l1  $l2\n" bg1
        } else {
            $t insert end "$l1  $l2\n"
        }
    }
    set erg [expr $count > 1 ? {[set s($lang)(results)]} : {[set s($lang)(result)]}]
    .statusBar.lab config  -foreground $ncolor -text "$count $erg $more"

    # find match pattern
    regsub -all {[,+]} $query "|" mquery
    regsub -all { } $mquery "" mquery

    # mark search words
    set cur 1.0
    while 1 {
        set cur [.result.text search -nocase -count length -regexp -- $mquery $cur end]
        if {$cur == ""} {
            break
        }
        .result.text tag add matchfg $cur "$cur + $length char"
        set cur [.result.text  index "$cur + $length char"]
    }
    .result.text configure -state disabled
}

bind dlookup <Double-1> {
    global query
    set tkPriv(selectMode) word
    tkTextSelectTo %W %x %y
    catch {%W mark set insert sel.first}
    catch {set q [selection get -displayof %W]}
    if {[string length $q] > 0 && [string compare $query $q]} {
        set query $q 
        dictsearch $query
    }
}

bind dlookup <2> { 
    global query
    set tkPriv(x) %x
    set tkPriv(y) %y 
    catch {set q [selection get]}
    if {[info exists q] && [string length $q] > 0 && [string compare $query $q]} {
        set query $q
        dictsearch $query
    }
}

# catch resize event to redisplay result text
bind dlookup <Configure> { 
    if { $inshistory > 0 && [string length $history_result($inshistory)] > 0} {
        display $history_result($inshistory) $history_query($inshistory)
    }  
}

proc save {what} {
    global inshistory history_result maxhistory s lang
 
    if { $inshistory <= 0 || [string length $history_result($inshistory)] <= 0} {
	tk_messageBox -icon info -type ok -parent . -message \
        	"Noch keine Suchergebnisse zum Abspeichern."
	return
    }
    set f [tk_getSaveFile]
    if {$f == ""} {
        return
    }
    set err [catch "set fd \[open $f w\]"]
    if $err {
	tk_messageBox -icon error -type ok -parent . -message \
        	"Couldn't open $f for writing!"
        return
    }
    if {$what == 0} {		# current result
        puts $fd $history_result($inshistory)
    } else {                    # all results in history
        for {set h $inshistory} {$h <= [array size history_result]} {incr h} {
            puts $fd $history_result($h)
        }
        for {set h 1} {$h <= $inshistory} {incr h} {
            puts $fd $history_result($h)
        }
    }
    catch {close $fd}
}

proc aboutBox {} {
    global lfont version bfont pname s lang 
    if ![winfo exists .about] {
        toplevel .about
        wm title .about "ber $pname"
        wm resizable .about 0 0
        text .about.text -font $lfont -wrap word -width 60 -height 26 \
            -relief groove -padx 10 -pady 10
        button .about.ok -text "Ok" -width 8 -command {
            after cancel animlogo .about.text.logo
            destroy .about}
        pack .about.text -side top -expand 1 -fill x -fill y -ipady 10 -ipadx 10
        pack .about.ok -side bottom -pady 8
        label .about.text.logo -image logo1 -bd 1
        animlogo .about.text.logo

        .about.text tag configure center -justify center
        .about.text tag configure bfont -font $bfont -justify center

        .about.text insert end " " center
        # .about.text image create end -image logo1 -pady 10 -align center
        .about.text window create end -window .about.text.logo -pady 10 -align center
        .about.text insert end " \n$pname\nVersion $version\n" bfont
        .about.text insert end "
Copyright (c) 1999 Frank Richter <Frank.Richter@hrz.tu-chemnitz.de>\n\n\
Online: http://www.tu-chemnitz.de/urz/netz/forms/dict.html\n
Kommentare sind sehr willkommen!
Comments are welcome!\n
Dieses Programm wurde unter der GNU General Public License (GPL Version 2) \
verffentlicht. \
Fr die Arbeitsweise des Programmes und die Richtigkeit der Daten wird \
keinerlei Garantie bernommen.

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. This program is distributed in the hope that \
it will be useful, but WITHOUT ANY WARRANTY; without even the implied \
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See \
the GNU General Public License for more details.
        " center
        .about.text configure -state disabled
    } else {
        wm deiconify .about
        raise .about 
    }
}

proc helpBox {} {
    global lfont version bfont pname s lang 
    if ![winfo exists .help] {
        toplevel .help
        wm title .help "$pname - [set s($lang)(help)]"
        # wm resizable .help 0 0
        text .help.text -font $lfont -wrap word -width 80 -height 24 \
            -relief groove -yscrollcommand ".help.yscroll set" \
            -padx 4 -pady 4
        scrollbar .help.yscroll -orient vertical -width 10 \
            -command { .help.text yview }         
        button .help.ok -text "Ok" -width 8 -command {destroy .help}
        pack .help.ok -side bottom -pady 8 
        pack .help.yscroll -side right -fill y
        pack .help.text -expand 1 -fill both

        .help.text tag configure center -justify center
        .help.text tag configure big -font $bfont -justify center
        .help.text tag configure bfont -font $bfont
        .help.text tag configure maillink -foreground blue -underline 1
        .help.text tag bind maillink <ButtonRelease-1> { sendMail }
        .help.text tag bind maillink <Enter> { .help.text config -cursor hand2}
        .help.text tag bind maillink <Leave> { .help.text config -cursor xterm}

        .help.text insert end "\n$pname - [set s($lang)(help)]\nVersion $version\n\n" big

        if {$lang == "de"} {
        .help.text insert end {Suche starten:} bfont
        .help.text insert end {
   * Suchwort eingeben, ENTER oder auf "Suche" drcken, oder
   * Doppelklick mit linker Maustaste auf Wort in Ergebnisfenster, oder
   * in anderem Fenser selektiertes Wort mit mittlerer Maustaste ber 
     Ergebnisfenster "fallenlassen"
}
        .help.text insert end {
Suchworte angeben: } bfont
        .help.text insert end {
   * ein Suchwort oder
   * eins aus mehreren Wrtern (ODER-Verknpfung): }
        .help.text insert end {Wort1,Wort2} bfont
        .help.text insert end {
   * alle Wrter (UND-Verknpfung): }
        .help.text insert end {Wort1+Wort2} bfont
        .help.text insert end {
   * exakte Wortfolge: }
        .help.text insert end {Wort1 Wort2} bfont

        .help.text insert end {

Suchoptionen: } bfont
        .help.text insert end {
   * Suche nach vollstndigen Wrtern oder nach Muster in Wrtern?
   * Gro-/Kleinschreibweise ignorieren oder exakt beachten?
   * Korrekte Schreibweise oder, falls agrep benutzt wird, Fehlerkorrektur 
     versuchen?
   * Einfache Suche (* als Platzhalter fr beliebige Zeichen) oder 
     regulre Ausdrcke zulassen?
}
        .help.text insert end {
History-Funktion: } bfont
        .help.text insert end {
   * Frhere Suchergebnisse lassen sich wieder anzeigen (Knpfe "<" bzw.  ">")
}
        .help.text insert end {
Zur Funktionsweise: } bfont
        .help.text insert end "
   * \"$pname\" ist kein intelligentes Dolmetscherprogramm, 
     sondern letztlich nur ein Front-End zur Suche in Dateien.
   * Die eigentliche Suche fhrt ein dafr existierendes Unix-Kommando aus der 
     \"grep\"-Famile durch. Hat man \"agrep\" installiert, kann man die Funktion 
     der \"fehlertoleranten Suche\" nutzen.
   * Die Ergebnisse sind nur so gut, wie die zugrundeliegende Wrterbuch-Datei.
     Zur Verbesserung dieser Datenbasis knnen Sie beitragen. 
     Senden Sie einfach "
        .help.text insert end {eine E-Mail an den Autor.} maillink

        .help.text insert end {

Weitere Informationen: } bfont
        .help.text insert end {
   * Unix-Manuals zu agrep und egrep 

        }

        } else {                      # english
        .help.text insert end {Start search:} bfont
        .help.text insert end {
   * Type in search word, then press ENTER or press the "Search" button, or
   * Double click on a word in the result window with mouse button 1, or
   * Select a word in another X window, drop it by clicking with mouse
     button 2 over the result window.
}
        .help.text insert end {
Give search words: } bfont
        .help.text insert end {
   * just one word, or
   * Find one of many words: }
        .help.text insert end {word1,word2} bfont
        .help.text insert end {
   * Find all words: }
        .help.text insert end {word1+word2} bfont
        .help.text insert end {
   * Find exact phrase: }
        .help.text insert end {word1 word2} bfont

        .help.text insert end {

Search options: } bfont
        .help.text insert end {
   * Search for full words or partial matches within words?
   * Ignore case or search case sensitive?
   * If agrep is used, try error correction if nothing is found?
   * Simple search (* is wildcard for any character) or search with
     regular expressions?
        }
        .help.text insert end {
History function: } bfont
        .help.text insert end {
   * Previous search results could be displayed again ("<" and ">" buttons)
        }
        .help.text insert end {
How it works: } bfont
        .help.text insert end "
   * \"$pname\" is not an intelligent translator system, but \"only\"
     a front-end to search quickly in files.
   * The search itself is done by a dedicated Unix command  ala \"grep\".
     If you have installed \"agrep\" you are able to use the approximate
     matching feature to correct spelling errors.
   * Well, the results are as good as the dictionary wordlist. 
     You could contribute to improve this list!
     Simply send "
        .help.text insert end {an email to the author.} maillink

        .help.text insert end {

Further information: } bfont
        .help.text insert end {
   * Unix manual pages for agrep and egrep 

        }
        }
        .help.text configure -state disabled
    } else {
        wm deiconify .help
        raise .help 
    }
}

proc setOptions {} {
    global lfont dictfile dictfiles minlength maxlength maxresults maxhistory
    global pname separator bfont language1 language2 s lang grepcmds grepcmd
    global grepopts default_grepcmd

    # temporary variables
    variable ogrepcmd   $grepcmd
    variable ogrepopts  $grepopts
    variable odictfile  $dictfile
    variable oseparator $separator
    variable olanguage1 $language1
    variable olanguage2 $language2
    variable ominlength $minlength
    variable omaxlength $maxlength
    variable omaxresults $maxresults
    variable omaxhistory $maxhistory

    if ![winfo exists .sopts] {
        toplevel .sopts
        wm title .sopts "$pname: [set s($lang)(osearcho)]"
        wm iconname .sopts "dict"
        frame .sopts.buttons
        pack .sopts.buttons -side bottom -fill x
        button .sopts.buttons.ok -text [set s($lang)(apply)] -command {
            set grepcmd $ogrepcmd
            set grepopts $ogrepopts
            set dictfile $odictfile
            set dictfiles [glob -nocomplain $dictfile]
            set separator $oseparator
            set language1 $olanguage1
            set language2 $olanguage2
            set minlength $ominlength
            set maxlength $omaxlength
            set maxresults $omaxresults
            set maxhistory $omaxhistory
            destroy .sopts
        }
        button .sopts.buttons.st -text [set s($lang)(default)] -command {
            set ogrepcmd  $default_grepcmd
            set ogrepopts $default_grepopts
            set odictfile $default_dictfile
            set oseparator $default_separator
            set olanguage1 $default_language1
            set olanguage2 $default_language2
            set ominlength $default_minlength
            set omaxlength $default_maxlength
            set omaxresults $default_maxresults
            set omaxhistory $default_maxhistory
        }
        button .sopts.buttons.cancel -text [set s($lang)(cancel)] -command "destroy .sopts"
        pack .sopts.buttons.ok .sopts.buttons.st .sopts.buttons.cancel \
            -side left -expand 1 -pady 8

        # Command
        frame .sopts.c -bd 2 -relief groove
        pack .sopts.c -side top -fill x -ipady 10 -ipady 10
        label .sopts.c.l -text "[set s($lang)(grepcmd)]:" -font $lfont
        variable clabel
        menubutton .sopts.c.m -textvariable ogrepcmd -menu .sopts.c.m.m \
            -indicatoron 1 -relief raised -anchor c -direction flush \
            -font $lfont -width 13
        menu .sopts.c.m.m -font $lfont -tearoff 0
        foreach c $grepcmds {
            .sopts.c.m.m add command -label $c -font $lfont \
                -command "set ogrepcmd $c"
        }
        label .sopts.c.l2 -text "[set s($lang)(grepopts)]:" -font $lfont
        entry .sopts.c.o -textvariable ogrepopts -font $lfont -width 5
        grid .sopts.c.l -in .sopts.c -row 0 -column 0 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .sopts.c.m -in .sopts.c -row 0 -column 1 -rowspan 1 -sticky w \
            -padx 4 -pady 4
        grid .sopts.c.l2 -in .sopts.c -row 0 -column 2 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .sopts.c.o -in .sopts.c -row 0 -column 3 -rowspan 1 -sticky w \
            -padx 4 -pady 4

        # File
        frame .sopts.file -bd 2 -relief groove 
        pack .sopts.file -side top -fill x -ipadx 10 -ipady 10 -pady 10
        label .sopts.file.l -text "[set s($lang)(dictfile)]: " -font $bfont
        entry .sopts.file.e -width 36 -font $lfont -textvariable odictfile
        variable types {
            {"Text files" {.txt}}
            {"All files" *}
        }

        button .sopts.file.b -font $lfont -text "Browse ..." \
            -command {
                set f [tk_getOpenFile -initialdir [file dirname $odictfile] \
                    -filetypes $types -parent .sopts]
                if [string compare $f ""] {
                    set odictfile $f
                }
            }
        label .sopts.file.lt -text "[set s($lang)(sep)]:" -font $lfont
        label .sopts.file.l1 -text "[set s($lang)(lang)] 1:" -font $lfont
        label .sopts.file.l2 -text "[set s($lang)(lang)] 2:" -font $lfont
        entry .sopts.file.et -font $lfont -textvariable oseparator -width 5
        entry .sopts.file.e1 -font $lfont -textvariable olanguage1 -width 10
        entry .sopts.file.e2 -font $lfont -textvariable olanguage2 -width 10

        grid .sopts.file.l -in .sopts.file -row 0 -column 0 -columnspan 6
        grid .sopts.file.e -in .sopts.file -row 1 -column 0 -columnspan 5 -sticky e
        grid .sopts.file.b -in .sopts.file -row 1 -column 6 
        grid .sopts.file.l1 -in .sopts.file -row 2 -column 0 -padx 4 -columnspan 2
        grid .sopts.file.lt -in .sopts.file -row 2 -column 2 -padx 4 -columnspan 2
        grid .sopts.file.l2 -in .sopts.file -row 2 -column 4 -padx 4 -columnspan 2
        grid .sopts.file.e1 -in .sopts.file -row 3 -column 0 -padx 4 -columnspan 2
        grid .sopts.file.et -in .sopts.file -row 3 -column 2 -padx 4 -columnspan 2
        grid .sopts.file.e2 -in .sopts.file -row 3 -column 4 -padx 4 -columnspan 2
        

        frame .sopts.search -bd 2 -relief groove
        pack .sopts.search -side top -fill x -ipadx 10 -ipady 10
        label .sopts.search.rl -text "[set s($lang)(maxresults)]:" -font $lfont
        entry .sopts.search.re -justify right -width 5 -font $lfont -textvariable omaxresults
        label .sopts.search.ll -text "[set s($lang)(minlength)]:" -font $lfont
        entry .sopts.search.le -justify right -width 5 -font $lfont -textvariable ominlength
        label .sopts.search.ml -text "[set s($lang)(maxlength)]:" -font $lfont
        entry .sopts.search.me -justify right -width 5 -font $lfont -textvariable omaxlength
        label .sopts.search.hl -text "[set s($lang)(maxhistory)]:" -font $lfont
        entry .sopts.search.he -justify right -width 5 -font $lfont -textvariable omaxhistory

        grid .sopts.search.rl -in .sopts.search -row 0 -column 0 -rowspan 1 -sticky e -padx 0
        grid .sopts.search.re -in .sopts.search -row 0 -column 1 -rowspan 1 -sticky w -padx 0
        grid .sopts.search.ll -in .sopts.search -row 1 -column 0 -rowspan 1 -sticky e -padx 0
        grid .sopts.search.le -in .sopts.search -row 1 -column 1 -rowspan 1 -sticky w -padx 0
        grid .sopts.search.ml -in .sopts.search -row 2 -column 0 -rowspan 1 -sticky e -padx 0
        grid .sopts.search.me -in .sopts.search -row 2 -column 1 -rowspan 1 -sticky w -padx 0
        grid .sopts.search.hl -in .sopts.search -row 3 -column 0 -rowspan 1 -sticky e -padx 0
        grid .sopts.search.he -in .sopts.search -row 3 -column 1 -rowspan 1 -sticky w -padx 0
        
        
    } else {
        wm deiconify .sopts
        raise .sopts
    }
}

proc setGeneral {} {
    global default_fcolor default_bcolor fcolor bcolor
    global pname lfont s lang languages tfont tfonts default_tfont
    global balloonDelay showBalloons default_balloonDelay default_showBalloons 
    global shapedresult default_shapedresult

    if ![winfo exists .general] {
        toplevel .general
        wm title .general "$pname: [set s($lang)(general)]"
        wm iconname .general "ding"

        variable fc "$fcolor"
        variable bc "$bcolor"
        variable ll $lang
        variable ffamily
        variable fsize
        scan $tfont "%s %d" ffamily fsize

        frame .general.buttons
        pack .general.buttons -side bottom -fill x

        button .general.buttons.ok -text [set s($lang)(apply)] -command {
            set lang $ll
            set tf "$ffamily $fsize"
            if [string compare $tf $tfont] {
                set tfont "$tf"
                .result.text configure -font "$tfont"
            }
            if {[string compare $fcolor $fc] || [string compare $bcolor $bc]} {
                set fcolor "$fc"
                set bcolor "$bc"
                tk_setPalette foreground "$fc" background "$bc" 
                if {[winfo depth .] > 1} {      # Color display
                    # other background color for result text
                    .result.text tag configure bg1 -background \
                        [shadeColor $bcolor]
                }
            }
            destroy .general
        }
        button .general.buttons.st -text [set s($lang)(default)] -command {
            set lang $default_language
            set llabel $languages($lang)
            set fc $default_fcolor
            set bc $default_bcolor
            .general.c.fl configure -background $fc
            .general.c.bl configure -background $bc
            scan $default_tfont "%s %d" ffamily fsize
            set balloonDelay $default_balloonDelay
            set showBalloons $default_showBalloons
        }
        button .general.buttons.cancel -text [set s($lang)(cancel)] \
            -command "destroy .general"
        pack .general.buttons.ok .general.buttons.st .general.buttons.cancel \
            -side left -expand 1 -pady 8 -padx 8

        # language
        frame .general.l -bd 2 -relief groove
        pack .general.l -side top -fill x -ipady 10 -ipady 10
        label .general.l.l -text "[set s($lang)(lang)]:" -font $lfont
        variable llabel
        set llabel $languages($lang)
        menubutton .general.l.m -textvariable llabel -menu .general.l.m.m \
            -indicatoron 1 -relief raised -anchor c -direction flush \
            -font $lfont -width 13
        menu .general.l.m.m -font $lfont -tearoff 0
        foreach i [array names languages] {
            .general.l.m.m add command -label $languages($i) -font $lfont \
                -command "set ll $i; set llabel $languages($i)"
        }
        grid .general.l.l -in .general.l -row 0 -column 0 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .general.l.m -in .general.l -row 0 -column 1 -rowspan 1 -sticky w \
            -padx 4 -pady 4

        # colors
        frame .general.c -bd 2 -relief groove
        pack .general.c -side top -fill x -pady 10 -ipady 10 -ipady 10

        button .general.c.fb -text [set s($lang)(fg)] -width 18 -font $lfont \
            -command {
                set fc [selectColor $fc fg]
                if [string compare $fc ""] {
                    .general.c.fl configure -background $fc
                }
            }
        button .general.c.bb -text [set s($lang)(bg)] -width 18 -font $lfont \
            -command {
                set bc [selectColor $bc bg]
                if [string compare $bc ""] {
                    .general.c.bl configure -background $bc
                }
            }
        label .general.c.fl -background $fc -width 8 -relief groove
        label .general.c.bl -background $bc -width 8 -relief groove
        grid .general.c.fb -in .general.c -row 0 -column 0 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .general.c.fl -in .general.c -row 0 -column 1 -rowspan 1 -sticky w \
            -padx 4 -pady 4
        grid .general.c.bb -in .general.c -row 1 -column 0 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .general.c.bl -in .general.c -row 1 -column 1 -rowspan 1 -sticky w \
            -padx 4 -pady 4

        # Font
        frame .general.f -bd 2 -relief groove
        pack .general.f -side top -fill x -ipady 10 -ipady 10
        frame .general.dummy
        pack .general.dummy -pady 5
        label .general.f.l -text "[set s($lang)(tfont)]: " -font $lfont

        menubutton .general.f.f -textvariable ffamily -menu .general.f.f.m \
            -indicatoron 1 -relief raised -anchor c -direction flush \
            -font $lfont -width 13
        menu .general.f.f.m -font $lfont -tearoff 0
        set allfams [font families]
        foreach i $tfonts {
            if {[lsearch $allfams [string tolower $i]] != -1} {
                .general.f.f.m add command -label $i -font $lfont \
                    -command "set ffamily $i"
            }
        }
        menubutton .general.f.s -textvariable fsize -menu .general.f.s.m \
            -indicatoron 1 -relief raised -anchor c -direction flush \
            -font $lfont -width 4
        menu .general.f.s.m -font $lfont -tearoff 0
        foreach i {8 10 11 12 13 14 15 18} {
            .general.f.s.m add command -label "$i" -font $lfont \
                -command "set fsize \"$i\""
        }
        checkbutton .general.f.shaped -text [set s($lang)(shaped)] -font $lfont \
            -variable shapedresult
        grid .general.f.l -in .general.f -row 0 -column 0 -rowspan 1 -sticky e \
            -padx 4 -pady 4
        grid .general.f.f -in .general.f -row 0 -column 1 -rowspan 1 -sticky w \
            -padx 4 -pady 4
        grid .general.f.s -in .general.f -row 0 -column 2 -rowspan 1 -sticky w \
            -padx 4 -pady 4
        grid .general.f.shaped -in .general.f -row 1 -column 0 -rowspan 1 \
            -columnspan 3 -sticky w -padx 4 -pady 4
        # pack .general.f.shaped

        # Balloon help
        frame .general.b -bd 2 -relief groove
        pack .general.b -side top -fill x -ipady 10 -ipady 10 
        checkbutton .general.b.help -text [set s($lang)(balloon)] -font $lfont \
            -variable showBalloons -command {
                if {$showBalloons == 1} {
                    .general.b.e configure -state normal -foreground black
                    .general.b.l1 configure -foreground black
                    .general.b.l2 configure -foreground black
                } else {
                    .general.b.e configure -state disabled -foreground gray
                    .general.b.l1 configure -foreground gray
                    .general.b.l2 configure -foreground gray
                }
             }

        label .general.b.l1 -text [set s($lang)(after)] -font $lfont
        entry .general.b.e -width 5 -textvariable balloonDelay -justify right
        label .general.b.l2 -text [set s($lang)(ms)] -font $lfont
        pack .general.b.help .general.b.l1 .general.b.e .general.b.l2 \
            -side left -expand 1 -fill x -padx 4 -pady 4
        
    } else {
        wm deiconify .general
        raise .general
    }
}

proc selectColor {color name} {
    global s lang 
    grab .
    set color [tk_chooseColor -title "[set s($lang)(color)] [set s($lang)($name)]" \
        -initialcolor $color]
    grab release .
    if [string compare $color ""] {
        return $color
    }
}

# Save options 
proc saveOptions {} {
    global balloonDelay showBalloons dictfile maxlength maxresults minlength
    global maxhistory separator language1 language2 pname fcolor bcolor
    global env s lang tfont shapedresult grepcmd grepopts

    if {[file readable $env(HOME)/.dingrc] &&
        ![file isfile $env(HOME)/.dingrc]} {
       puts stderr "$env(HOME)/.dingrc isn't a regular file!"
       return
    }
    set err [catch "set fd \[open $env(HOME)/.dingrc w\]"]
    if $err {
        puts stderr "Couldn't open $env(HOME)/.dingrc for writing!"
        return
    }
    puts $fd "# Options for $pname - do not edit!"
    puts $fd "# Dictionary"
    puts $fd "set dictfile $dictfile"
    puts $fd "set separator \"$separator\""
    puts $fd "set language1 $language1"
    puts $fd "set language2 $language2"
    puts $fd "# search options"
    puts $fd "set grepcmd {$grepcmd}"
    puts $fd "set grepopts {$grepopts}"
    puts $fd "set maxlength $maxlength"
    puts $fd "set maxresults $maxresults"
    puts $fd "set maxhistory $maxhistory"
    puts $fd "set minlength $minlength"
    puts $fd "# General options"
    puts $fd "set lang $lang"
    puts $fd "set fcolor $fcolor"
    puts $fd "set bcolor $bcolor"
    puts $fd "set tfont {$tfont}"
    puts $fd "set shapedresult $shapedresult"
    puts $fd "set showBalloons $showBalloons"
    puts $fd "set balloonDelay $balloonDelay"

    catch {close $fd}
    .statusBar.lab configure -text "[set s($lang)(saveopts)] ok"
}

# History function
proc history {where} {
    global curhistory inshistory history_query history_result maxhistory
    global ncolor lang s
    set len [array size history_result]

    if ![string compare $where "back"] { # back
        if {$curhistory == 0} {
            set curhistory $inshistory
        }
        # puts "back: cur $curhistory ins $inshistory length $len\n"
        if {($curhistory <= 0) ||
            ($curhistory == 1 && $len < $maxhistory) ||
            ($curhistory == 1 && $inshistory == $maxhistory)} {
                # no more history entries
                .statusBar.lab config -foreground $ncolor \
                    -text [set s($lang)(noback)]
                return
        }
        if {$curhistory == 1} {
            set curhistory $maxhistory
        } else {
            set curhistory [expr $curhistory - 1]
        }
        display $history_result($curhistory) $history_query($curhistory)
        if {($curhistory == 1 && ($len < $maxhistory || $inshistory == $maxhistory)) || 
            ($inshistory == [expr $curhistory - 1])} {
            .search.s.back configure -state disabled
        }
        .search.s.forw configure -state normal
    } else {            # forward
        # puts "forw: cur $curhistory ins $inshistory length $len\n"
        if {($curhistory <= 0) || ($len <= $curhistory)} {
                # no more history entries
                .statusBar.lab config -foreground $ncolor \
                    -text [set s($lang)(noforw)]
                return
        }
        if {$curhistory == $maxhistory} {
            set curhistory 1
        } else {
            incr curhistory
        }
        display $history_result($curhistory) $history_query($curhistory)
        if {($curhistory >= $maxhistory && $maxhistory < $len) || 
            $curhistory == $inshistory} {
            .search.s.forw configure -state disabled
        }
        .search.s.back configure -state normal
    }
}

proc sendMail {} {
    global author authormail lfont version bfont pname s lang 
    variable subject "$pname $version: "

    if ![winfo exists .mail] {
        toplevel .mail
        wm title .mail "[set s($lang)(mailtitle)] $author"
        wm resizable .mail 0 0

        frame .mail.buttons
        button .mail.buttons.ok -text [set s($lang)(send)] -width 15 -command {
            set text [.mail.t.text get 1.0 end]
            if {[string length $text] <= 3} {
	        tk_messageBox -icon warning -type ok -parent .mail -message \
                    [set s($lang)(notext)]
        
                return
            }
            catch {set fd [open "|mail -s \"$subject\" $authormail" w]} em
            if ![info exists fd] {
                catch {set fd [open "|mail $tkdesk(authormail)" w]} em
            }
            if ![info exists fd] {
                tk_messageBox -icon error -type ok -parent .mail -message \
                    [set s($lang)(notext)]
            } else {
                puts -nonewline $fd $text
                close $fd
            }
            destroy .mail
        }
        button .mail.buttons.cancel -text [set s($lang)(cancel)] -width 15 \
            -command {destroy .mail}
        pack .mail.buttons -side bottom -expand 1 -padx 8 -pady 8
        pack .mail.buttons.ok .mail.buttons.cancel -side left -padx 8

        frame .mail.s
        entry .mail.s.subject -bg white -textvariable subject
        label .mail.s.label -text "Subject: "
        pack .mail.s -side top -fill x -expand 1 -padx 8 -pady 8
        pack .mail.s.label -side left
        pack .mail.s.subject -side left -fill x -expand 1
        
        frame .mail.t
        text .mail.t.text -wrap word -width 70 -height 15 \
            -relief groove -bg white \
            -yscrollcommand ".mail.t.yscroll set"

        scrollbar .mail.t.yscroll -orient vertical -width 10 \
                -command { .mail.t.text yview }                                                          
        pack .mail.t -fill x -expand 1 -padx 8 -pady 8
        pack .mail.t.text .mail.t.yscroll -side left -fill both -expand 1

    } else {
        wm deiconify .mail
        raise .mail 
    }
}


# Settings for scrolling with a wheel mouse
# See http://www.inria.fr/koala/colas/mouse-wheel-scroll/
proc mscroll {bindtag} {
    bind $bindtag <Button-5> [list %W yview scroll 5 units]
    bind $bindtag <Button-4> [list %W yview scroll -5 units]
    bind $bindtag <Shift-Button-5> [list %W yview scroll 1 units]
    bind $bindtag <Shift-Button-4> [list %W yview scroll -1 units]
    bind $bindtag <Control-Button-5> [list %W yview scroll 1 pages]
    bind $bindtag <Control-Button-4> [list %W yview scroll -1 pages]
}
mscroll Text

# Thanks to Holger Trapp <hot@informatik.tu-chemnitz.de>
# enforce loading the Tcl procedures "tcl_wordBreakBefore" and
# "tcl_wordBreakAfter" by calling them with senseless args
tcl_wordBreakBefore "" 0
tcl_wordBreakAfter "" 0

# now the global strings "tcl_nonwordchars" and "tcl_wordchars" are
# initialized
# and we can modify them as needed to specify the chars belonging or not
# belonging to words respectively
set tcl_wordchars {[a-zA-Z0-9_]}
set tcl_nonwordchars {[^a-zA-Z0-9_]}

