Updated 2016-07-06 18:45:48 by JayBy

JayBy 2016-07-06

I've written this script to inhibit the mental arithmetic during a x01 game, because I need my concentration to hit the board ;-)

You can edit every shot or the summery of three shots. The needed rows will be created on the fly, if needed. At the bottom, it shows the possible finishes for 'double out'.

It only requires Tk. Other packages are optional.

In this script, I use labels outside of a gridded frame. An extended version of this concept can be found on gridLabels.

#******************************************************************************
#           X01 - Darts
#
# Description:
#   Simple practice Darts app for x01 games.
#
#   It can be edit ...
#   - shot by shot with acronyms. like t20, d20, sb, db ...
#       (The calculations will be done automatically)
#   - or the summary of the 3 darts.
#
#   The needed rows will be created on the fly, if necessary, after typing
#   <Tab> or <Return>
#
#   The key bindings (Return, Tab, Shift-Tab) will hold
#   the focus either in the shots entries or in the sum entries, depending
#   what was edited.
#   Arrow keys traverses all shots and points entries.
#
#   Possible finishes (double out) will be shown in statusbar.
#
# Requirements:
#   Tcl/Tk >= 8.5
# Optional:
#   scrolledframe
#   defColor >= 0.3
#   xtk::theme >= 0.2
#   xtk::theme::rivet >= 0.2
#
# Written by Juergen "Arthur" Beyersdorff, 2016, Stuttgart, Germany

#==============================================================================
#           Settings
#

#---    Packages    ----------------------------------------

set App(Packs,Required) ""
set App(Packs,Optional) ""
set App(Packs,Loaded) ""

# Required
lappend App(Packs,Required) {Tk 8.5}
# Optional
lappend App(Packs,Optional) {scrolledframe}
lappend App(Packs,Optional) {defColor 0.3}
lappend App(Packs,Optional) {xtk::theme 0.2}
lappend App(Packs,Optional) {xtk::theme::rivet}

#---    Paths    -------------------------------------------

# Run with starkit or not ?
set App(Path,TopDir) [expr { [info exists starkit::topdir] ? $starkit::topdir : [file dirname [file dirname [file normalize [info script]]]] } ]

set App(Path,BinDir) [file join $App(Path,TopDir) bin]
set App(Path,LibDir) [file join $App(Path,TopDir) lib]

# auto_path
if {[lsearch -exact $auto_path $App(Path,LibDir)] < 0} {
    lappend auto_path        $App(Path,LibDir)
}

#---    Game Values ----------------------------------------

set X01(Rest0)  501
set X01(SbarMsg)    ""

#==============================================================================
#           Configure
#

#---    Load Packages    -----------------------------------

puts "Load required packages :"
foreach P $App(Packs,Required) {
    lassign $P N V
    package require {*}$P
    lappend App(Packs,Loaded) [list $N [package provide $N]]
    puts "    [list $N [package provide $N]]"
}

puts "Load optional packages :"
foreach P $App(Packs,Optional) {
    lassign $P N V
    if {[catch {package require {*}$P} e]} {
        puts "    $N -> $e"
    } else {
        lappend App(Packs,Loaded)        [list $N [package provide $N]]
        puts "    [list $N [package provide $N]]"
    }
}

# Import namespaces
catch {namespace import ::scrolledframe::scrolledframe}

#---    Fonts   --------------------------------------------

font create SmallFont {*}[font actual TkDefaultFont]
font create BoldFont {*}[font actual TkDefaultFont]
font create RestFont {*}[font actual TkDefaultFont]
set Size    -12
font configure TkDefaultFont -size [string trim $Size -]
font configure SmallFont -size [string trim [expr int($Size * 0.9)] -]
font configure RestFont -size [string trim [expr int($Size * 1.3)] -]
font configure RestFont -weight bold

#---    Themes    ------------------------------------------

catch {::xtk::theme::ttk::init rivet2d}

#---    Icons   --------------------------------------------

set Icon(Reset) [image create photo -data {
    R0lGODlhDAAMAPUAADU1NTs7Oz09PT4+Pj8/P0BAQEFBQUJCQkhISFJSUllZ
    WV9fX2FhYWNjY2dnZ21tbXl5eampqaqqqre3t7u7u8nJyc7Ozs/Pz9DQ0NHR
    0dLS0tPT09TU1NXV1dfX19jY2NnZ2dra2tzc3N3d3d7e3t/f3+Li4uPj4+Xl
    5ebm5ufn5+np6evr6+zs7O7u7u/v7/Hx8fPz8/X19ff39/r6+gAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADUALAAAAAAMAAwAAAZT
    wJpwSCRSTiQPpkJpDBs0GczFQpFCAKFDSr16QgThgnoKhUhog1CBOn3RV3Ut
    QcJcQ558uIbAfPN5GHsGgEoYhwFChIeHFhgWWUIQEZSVD0WYQ0EAOw==
    }]
set Icon(Exit) [image create photo -data {
    R0lGODlhDAAMAPUAAB0TAhsUAR0UAR0UAh4ZDB8aDB8cDycbAiodAiYfDSog
    ByshCSkhDD0rAykkFjErGjowFEk1BUIyClQ/DUs8GXhVBnBQCHhZEU1CJFlL
    ImxZJW1bK3RhKXhtQYBbB4BfEYtnFIJsLYJxPMyRDM+SDNWeINGiL9enM9qr
    N9yuOtuvPdqvP+G5TeXEYujOdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAC8ALAAAAAAMAAwAAAY+
    wJdwSCwai44OQUjgMIgPlyhBCK0WRUxro1lJjhlWanJ8UVClyBFyulhIjSLD
    BAoEPKQDcXHRvwIVCGWDg0EAOw==
    }]

#==============================================================================
#           Procedures
#

#------------------------------------------------------------------------------
#           Darts specific procs and calcs
#

#---    Calculate rest points   ----------------------------

proc calcRest {{H 1}} {
    global X01
    while {1} {
        if {[info exists X01(Points$H)] == 0} {
            break
        } else {
            if {[string is integer -strict $X01(Points$H)]} {
                set X01(Rest$H)   [expr $X01(Rest[expr {$H - 1}]) - $X01(Points$H)]
            } else {
                set X01(Rest$H) ""
            }
        }
        incr H
    }
    return $X01(Rest[expr {$H - 1}])
}

#---    Calculate Heat Summary  ----------------------------

proc calcHeat {H} {
    global X01
    set sum 0
    foreach S [list 1 2 3] {
        if {[info exists X01(Value$H-$S)] == 1 && $X01(Value$H-$S) != ""} {
            incr sum [calcAcronym $X01(Value$H-$S)]
        }
    }
    return $sum
}

#---    Calculate Acronyms  --------------------------------

proc calcAcronym {A} {
    switch -regexp -- [string tolower $A] {
        ^[s]?[0-9]+$  {
            set Nr [string map {s {} S {}} $A]
            set Ret [expr $Nr * 1]
        }
        ^[d]?[0-9]+$  {
            set Nr [string map {d {} D {}} $A]
            set Ret [expr $Nr * 2]
        }
        ^[t]?[0-9]+$  {
            set Nr [string map {t {} T {}} $A]
            set Ret [expr $Nr * 3]
        }
        ^sb$    {
            set Ret 25
        }
        ^db$  {
            set Ret 50
        }
        default  {
            set Ret 0
        }

    }
    return $Ret
}

#---    List Finishes   ------------------------------------

proc listFinish {Pts} {
    switch -- $Pts {
        170 {set Finish [list "T20 T20 Bull"]}
        167 {set Finish [list "T20 T19 Bull"]}
        164 {set Finish [list "T20 T18 Bull"]}
        161 {set Finish [list "T20 T17 Bull"]}
        160 {set Finish [list "T20 T20 D20"]}
        158 {set Finish [list "T20 T20 D19"]}
        157 {set Finish [list "T20 T19 D20"]}
        156 {set Finish [list "T20 T20 D18"]}
        155 {set Finish [list "T20 T19 D19"]}
        154 {set Finish [list "T20 T18 D20"]}
        153 {set Finish [list "T20 T19 D18"]}
        152 {set Finish [list "T20 T20 D16"]}
        151 {set Finish [list "T20 T17 D20"]}
        150 {set Finish [list "T20 T20 D15" "T20 T18 D18"]}
        149 {set Finish        [list "T20 T19 D16"]}
        148 {set Finish [list "T20 T20 D14"]}
        147 {set Finish [list "T20 T17 D18"]}
        146 {set Finish [list "T20 T18 D16"]}
        145 {set Finish [list "T20 T15 D20"]}
        144 {set Finish [list "T20 T20 D12"]}
        143 {set Finish [list "T20 T17 D16"]}
        142 {set Finish [list "T20 Bull D16" "T20 T14 D20"]}
        141 {set Finish [list "T20 T19 D12" "T20 T15 D18"]}
        140 {set Finish [list "T20 T16 D16" "T20 T20 D10"]}
        139 {set Finish [list "T20 T13 D20"]}
        138 {set Finish [list "T20 T18 D12"]}
        137 {set Finish [list "T20 T19 D10"]}
        136 {set Finish [list "T20 T20 D8"]}
        135 {set Finish [list "Bull T15 D20" "25 T20 Bull"]}
        134 {set Finish [list "T20 T14 D16"]}
        133 {set Finish [list "T20 T19 D8"]}
        132 {set Finish [list "Bull T14 D20" "25 T19 Bull"]}
        131 {set Finish [list "T20 T13 D16"]}
        130 {set Finish [list "T20 T20 D5" "T20 20 Bull"]}
        129 {set Finish [list "T19 T16 D12" "19 T20 Bull"]}
        128 {set Finish [list "T18 T14 D16" "18 T20 Bull"]}
        127 {set Finish [list "T20 T17 D8" "20 T19 Bull"]}
        126 {set Finish [list "T19 T19 D6" "19 T19 Bull"]}
        125 {set Finish [list "Bull 25 Bull" "25 T20 D20" "18 T19 Bull"]}
        124 {set Finish [list "T20 14 Bull" "20 T18 Bull"]}
        123 {set Finish [list "T19 16 Bull" "19 T18 Bull"]}
        122 {set Finish [list "T18 18 Bull"]}
        121 {set Finish [list "T20 11 Bull" "20 T17 Bull"]}
        120 {set Finish [list "T20 20 D20"]}
        119 {set Finish [list "T19 12 Bull" "19 T20 D20"]}
        118 {set Finish [list "T20 18 D20" "20 T20 D19"]}
        117 {set Finish [list "T20 17 D20" "20 T19 D20"]}
        116 {set Finish [list "T20 T16 D4" "20 T20 D18"]}
        115 {set Finish [list "T20 15 D20"]}
        114 {set Finish [list "T19 17 D20" "19 T19 D19"]}
        113 {set Finish [list "T19 T16 D4" "19 T18 D20"]}
        112 {set Finish [list "T20 12 D20" "20 T20 D16"]}
        111 {set Finish [list "T19 14 D20" "19 T20 D16"]}
        110 {set Finish [list "T20 Bull" "T20 10 D20" "20 T18 D18"]}
        109 {set Finish [list "T20 9 D20" "20 T19 D16"]}
        108 {set Finish [list "T19 11 D20" "19 T19 D16"]}
        107 {set Finish [list "T19 Bull" "T19 10 D20" "19 T20 D14"]}
        106 {set Finish [list "T20 10 D18" "20 T18 D16"]}
        105 {set Finish [list "T20 13 D16" "20 T15 D20"]}
        104 {set Finish [list "T18 Bull" "T19 7 D20" "T18 10 D20"]}
        103 {set Finish [list "T19 10 D18" "T17 12 D20"]}
        102 {set Finish [list "T20 10 D16" "20 Bull D16"]}
        101 {set Finish [list "T17 Bull" "T20 17 D12" "20 T19 D12"]}
        100 {set Finish [list "T20 D20" "Bull Bull" "20 T16 D16" "20 T20 D10"]}
         99 {set Finish [list "T19 10 D16" "19 T20 D10"]}
         98 {set Finish [list "T20 D19" "20 T18 D12"]}
         97 {set Finish [list "T19 D20" "19 T18 D12"]}
         96 {set Finish [list "T20 D18" "20 T20 D8"]}
         95 {set Finish [list "T19 D19" "Bull 13 D16" "25 20 Bull"]}
         94 {set Finish [list "T18 D20" "Bull 12 D16" "25 19 Bull"]}
         93 {set Finish [list "T19 D18" "Bull 11 D16" "25 18 Bull"]}
         92 {set Finish [list "T20 D16" "Bull 10 D16" "25 17 Bull"]}
         91 {set Finish [list "T17 D20" "Bull 17 D12" "25 16 Bull"]}
         90 {set Finish [list "Bull D20" "25 15 Bull"]}
         89 {set Finish [list "T19 D16" "19 20 Bull"]}
         88 {set Finish [list "T20 D14" "20 18 Bull"]}
         87 {set Finish [list "T17 D18" "17 20 Bull"]}
         86 {set Finish [list "T18 D16" "18 18 Bull"]}
         85 {set Finish [list "T15 D20" "15 20 Bull"]}
         84 {set Finish [list "T20 D12" "20 14 Bull"]}
         83 {set Finish [list "T17 D16" "17 16 Bull"]}
         82 {set Finish [list "Bull D16" "T14 D20" "25 17 D20"]}
         81 {set Finish [list "T19 D12" "19 12 Bull"]}
         80 {set Finish [list "T20 D10" "20 20 D20"]}
         79 {set Finish [list "T13 D20" "13 16 Bull"]}
         78 {set Finish [list "T18 D12" "18 20 D20"]}
         77 {set Finish [list "T19 D10" "19 18 D20"]}
         76 {set Finish [list "T20 D8" "20 16 D20"]}
         75 {set Finish [list "T17 D12" "17 18 D20"]}
         74 {set Finish [list "T14 D16" "14 20 D20"]}
         73 {set Finish [list "T19 D8" "19 14 D20"]}
         72 {set Finish [list "T16 D12" "16 16 D20"]}
         71 {set Finish [list "T13 D16" "13 18 D20"]}
         70 {set Finish [list "T18 D8" "18 12 D20"]}
         69 {set Finish [list "T15 D12" "15 14 D20"]}
         68 {set Finish [list "T20 D4" "20 8 D20"]}
         67 {set Finish [list "T17 D8" "17 10 D20"]}
         66 {set Finish [list "Bull D8" "T14 D12" "T10 D18" "10 16 D20"]}
         65 {set Finish [list "T19 D4" "25 D20" "T11 D16" "19 10 D18"]}
         64 {set Finish [list "T16 D8" "16 16 D16"]}
         63 {set Finish [list "T13 D12" "13 10 D20"]}
         62 {set Finish [list "T10 D16" "T18 D4" "10 12 D20"]}
         61 {set Finish [list "T19 D2" "T15 D8" "19 10 D16" "15 10 D18"]}
         60 {set Finish [list "T12 D12" "20 D20" "20 20 D10"]}
         59 {set Finish [list "19 D20"]}
         58 {set Finish [list "18 D20" "T18 D2"]}
         57 {set Finish [list "17 D20"]}
         56 {set Finish [list "T8 D16" "16 D20"]}
         55 {set Finish [list "15 D20"]}
         54 {set Finish [list "14 D20"]}
         53 {set Finish [list "13 D20" "17 D18"]}
         52 {set Finish [list "T12 D8" "12 D20"]}
         51 {set Finish [list "11 D20"]}
         50 {set Finish [list "T14 D4" "14 D18" "10 D20" "Bull"]}
         49 {set Finish [list "9 D20"]}
         48 {set Finish [list "16 D16"]}
         47 {set Finish [list "15 D16" "7 D20"]}
         46 {set Finish [list "14 D16" "T10 D8" "10 D18"]}
         45 {set Finish [list "13 D16"]}
         44 {set Finish [list "12 D16"]}
         43 {set Finish [list "11 D16" "3 D20" "19 D12" "7 D18"]}
         42 {set Finish [list "10 D16"]}
         41 {set Finish [list "9 D16" "17 D12"]}
         40 {set Finish [list "D20"]}
         39 {set Finish [list "7 D16"]}
         38 {set Finish [list "D19"]}
         37 {set Finish [list "5 D16"]}
         36 {set Finish [list "D18"]}
         35 {set Finish [list "3 D16"]}
         34 {set Finish [list "D17"]}
         33 {set Finish [list "1 D16"]}
         32 {set Finish [list "D16"]}
         31 {set Finish [list "15 D8"]}
         30 {set Finish [list "D15"]}
         29 {set Finish [list "13 D8"]}
         28 {set Finish [list "D14"]}
         27 {set Finish [list "11 D8"]}
         26 {set Finish [list "D13"]}
         25 {set Finish [list "9 D8"]}
         24 {set Finish [list "D12"]}
         23 {set Finish [list "7 D8"]}
         22 {set Finish [list "D11"]}
         21 {set Finish [list "5 D8"]}
         20 {set Finish [list "D10"]}
         19 {set Finish [list "3 D8"]}
         18 {set Finish [list "D9"]}
         17 {set Finish [list "1 D8"]}
         16 {set Finish [list "D8"]}
         15 {set Finish [list "7 D4"]}
         14 {set Finish [list "D7"]}
         13 {set Finish [list "5 D4"]}
         12 {set Finish [list "D6"]}
         11 {set Finish [list "3 D4"]}
         10 {set Finish [list "D5"]}
          9 {set Finish [list "1 D4"]}
          8 {set Finish [list "D4"]}
          7 {set Finish [list "3 D2"]}
          6 {set Finish [list "D3"]}
          5 {set Finish [list "1 D2"]}
          4 {set Finish [list "D2"]}
          3 {set Finish [list "1 D1"]}
          2 {set Finish [list "D1"]}
          0 {set Finish [list "Game Shot"]}
        default  {set Finish [list ""]}
    }
    return $Finish
}


#==============================================================================
#           GUI
#

#---    Main Gui    ----------------------------------------

proc mainGui {} {
    global W Icon X01
    set W(Top) .
    wm withdraw $W(Top)
    wm title $W(Top)    "X01 - Darts"
    wm protocol $W(Top) WM_DELETE_WINDOW "exit"

    $W(Top) configure -background "[ttk::style lookup . -background]"

    #--- Head Frame ---
    set W(HeadFrame)    [ttk::frame $W(Top).headframe \
                        -style TFrame \
                        ]
    set W(HeadFrame,Name)   [ttk::entry $W(HeadFrame).name \
                            -width 10 \
                            -justify center \
                            -takefocus 1 \
                            ]
    $W(HeadFrame,Name) insert 0 "<Name>"
    bind $W(HeadFrame,Name) <FocusIn> {%W selection range 0 end}
    bind $W(HeadFrame,Name) <Down> {focus $W(ScrFrame,1,1)}
    bind $W(HeadFrame,Name) <FocusOut> {%W selection clear}
    set W(HeadFrame,Rest0)  [ttk::entry $W(HeadFrame).rest0 \
                            -width 5 \
                            -font RestFont \
                            -justify center \
                            -takefocus 1 \
                            -textvariable X01(Rest0) \
                            ]
    bind $W(HeadFrame,Rest0) <FocusIn> {%W selection range 0 end}
    bind $W(HeadFrame,Rest0) <Down> {focus $W(ScrFrame,1,Points)}
    bind $W(HeadFrame,Rest0) <FocusOut> {%W selection clear}

    grid $W(HeadFrame,Name) -column 0 -row 0 -sticky we -padx 5
    grid $W(HeadFrame,Rest0) -column 1 -row 0 -sticky we -padx 5
    grid columnconfigure $W(HeadFrame) 0 -weight 3
    grid columnconfigure $W(HeadFrame) 1 -weight 1

    #--- Content Frame ---
    set W(ContFrame)    [ttk::frame $W(Top).contframe \
                        -style TFrame -padding 0]
    # Table Header
    set W(CapFrame)     [frame $W(ContFrame).capframe]

    set W(CapFrame,Shots)   [ttk::label $W(CapFrame).head0 \
                            -text "Shots" \
                            -anchor c \
                            -font SmallFont \
                            ]
    set W(CapFrame,Sum)     [ttk::label $W(CapFrame).head1 \
                            -text "Sum" \
                            -anchor c \
                            -font SmallFont \
                            ]
    set W(CapFrame,Rest)    [ttk::label $W(CapFrame).head2 \
                            -text "Rest" \
                            -anchor c \
                            -font SmallFont \
                            ]

    grid $W(CapFrame) -column 0 -columnspan 3 -row 0 -sticky nswe

    # Scrolledframe ? available ?
    if {[info exists ::scrolledframe::version] == 1} {
        set W(ScrFrame0)    [scrolledframe $W(ContFrame).scrframe \
                            -yscrollcommand "$W(ContFrame).vscroll set" \
                            ]
        catch {$W(ScrFrame0) configure -fill both}
        set W(ScrFrame) $W(ScrFrame0).scrolled
        $W(ScrFrame) configure -background "[. cget -background]"
        set W(VScroll)  [ttk::scrollbar $W(ContFrame).vscroll \
                        -orient vertical \
                        -command "$W(ScrFrame0) yview" \
                        ]

        grid $W(ScrFrame0) -column 0 -columnspan 3 -row 1 -sticky nswe
        grid $W(VScroll) -column 3 -row 1 -sticky ns

    } else {
        set W(ScrFrame0)    [ttk::frame $W(ContFrame).scrframe]
        set W(ScrFrame)     [ttk::frame $W(ScrFrame0).scrolled]

        grid $W(ScrFrame) -column 0 -row 0 -sticky nswe
        grid $W(ScrFrame0) -column 0 -columnspan 3 -row 1 -sticky nswe
    }
    grid columnconfigure $W(ScrFrame0) 0 -weight 1

    # Heat
    crHeat 1 $W(ScrFrame)

    grid columnconfigure $W(ContFrame) 0 -weight 1
    grid rowconfigure $W(ContFrame) 1 -weight 1

    #--- Statusbar (for finishes) ---
    set W(StatFrame)    [ttk::frame $W(Top).statframe]
    set W(StatFrame,Reset)  [ttk::button $W(StatFrame).reset \
                            -text "Reset" \
                            -image $Icon(Reset) \
                            -command "resetX01" \
                            ]
    set W(StatFrame,Msg)    [ttk::label $W(StatFrame).sbar \
                            -textvariable X01(SbarMsg) \
                            ]
    set W(StatFrame,Exit)   [ttk::button $W(StatFrame).exit \
                            -text "Exit" \
                            -image $Icon(Exit) \
                            -command "exit" \
                            ]

    grid $W(StatFrame,Reset) -column 0 -row 0 -padx 5
    grid $W(StatFrame,Msg) -column 1 -row 0 -sticky we -padx 5
    grid $W(StatFrame,Exit) -column 2 -row 0 -padx 5
    grid columnconfigure $W(StatFrame) 1 -weight 1

    #--- Show GUI ---

    grid $W(HeadFrame) -column 0 -row 0 -padx 5 -pady 5 -sticky we
    grid $W(ContFrame) -column 0 -row 1 -padx 5 -pady 5 -sticky nswe
    grid $W(StatFrame) -column 0 -row 2 -padx 5 -pady 5 -sticky we

    grid columnconfigure $W(Top) 0 -weight 1
    grid rowconfigure $W(Top) 1 -weight 1

    catch {$W(ScrFrame0) configure -width [expr [winfo reqwidth $W(ScrFrame)] + [winfo reqwidth $W(VScroll)]]}
    update
    wm geometry $W(Top) [winfo reqwidth $W(Top)]x450
    bind . <Configure> {+ gridHead}
    wm deiconify $W(Top)

}

#---    Create Heat ----------------------------------------
# Create new Row

proc crHeat {H P} {
    global W X01
    set W(ScrFrame,$H,Shots)  [ttk::frame $P.shots$H]
    foreach S [list 1 2 3] {
        set W(ScrFrame,$H,$S)  [ttk::entry $W(ScrFrame,$H,Shots).shot$H-$S \
                            -width 5 \
                            -justify center \
                            -font SmallFont \
                            -takefocus 1 \
                            -textvariable X01(Value$H-$S)]
        bind $W(ScrFrame,$H,$S) <FocusIn> "
            %W selection range 0 end
        "
        bind $W(ScrFrame,$H,$S) <Return> "
            focusCalc %W next $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Tab> "
            focusCalc %W next $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Right> "
            focusCalc %W right $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Down> "
            focusCalc %W down $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Shift-Tab> "
            focusCalc %W prev $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <ISO_Left_Tab> "
            focusCalc %W prev $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Left> "
            focusCalc %W left $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <Up> "
            focusCalc %W up $H $S
            break
        "
        bind $W(ScrFrame,$H,$S) <FocusOut> "
            %W selection clear
        "
    }
    set W(ScrFrame,$H,Points)    [ttk::entry $P.points$H \
                                -width 5 \
                                -justify center \
                                -takefocus 1 \
                                -textvariable X01(Points$H)]
        bind $W(ScrFrame,$H,Points) <FocusIn> "
            %W selection range 0 end
        "
    bind $W(ScrFrame,$H,Points) <Return> "
        focusCalc %W next $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Tab> "
        focusCalc %W next $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Right> "
        focusCalc %W right $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Down> "
        focusCalc %W down $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Shift-Tab> "
        focusCalc %W prev $H
        break
    "
    bind $W(ScrFrame,$H,Points) <ISO_Left_Tab> "
        focusCalc %W prev $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Left> "
        focusCalc %W left $H
        break
    "
    bind $W(ScrFrame,$H,Points) <Up> "
        focusCalc %W up $H
        break
    "
    bind $W(ScrFrame,$H,Points) <FocusOut> "
        %W selection clear
    "
    set W(ScrFrame,$H,Rest)  [ttk::label $P.rest$H \
                            -width 3 \
                            -font RestFont \
                            -anchor e \
                            -textvariable X01(Rest$H)]

    set R   [expr $H + 0]
    grid $W(ScrFrame,$H,1) -column 0 -row 0
    grid $W(ScrFrame,$H,2) -column 1 -row 0
    grid $W(ScrFrame,$H,3) -column 2 -row 0
    grid $W(ScrFrame,$H,Shots) -column 0 -row $R -padx 5
    grid $W(ScrFrame,$H,Points) -column 1 -row $R -padx 5
    grid $W(ScrFrame,$H,Rest) -column 2 -row $R -padx 5

    grid columnconfigure $W(ScrFrame) 0 -weight 1
    grid columnconfigure $W(ScrFrame) 1 -weight 1
    grid columnconfigure $W(ScrFrame) 2 -weight 1

    update
    catch {$W(ScrFrame0) yview moveto 1.0}
}

#---    Key Bindings    ------------------------------------
# Calculate values and set new focus.

proc focusCalc {Win D H {S ""}} {
    global W X01
    set P   [winfo parent $Win]
    # Calculation
    if {$S != "" && [$W(ScrFrame,$H,$S) get] != ""} {
        set X01(Points$H)    [calcHeat $H]
    }
    set Rest    [calcRest]
    if {$Rest > 0 && [regexp {(next|down)} $D]} {
        if {$S == ""} {
            if {[info exists W(ScrFrame,[expr $H + 1],Points)] == 0 \
            && [$Win get] != ""} {
                crHeat [expr $H + 1] $P
            }
        } elseif {$S == 3} {
            if {[info exists W(ScrFrame,[expr $H + 1],3)] == 0 \
            && [$Win get] != ""} {
                crHeat [expr $H + 1] [winfo parent $P]
            }
        }
    }
    if {$Rest <= 170} {
        set X01(SbarMsg)    [listFinish $Rest]
    }
    # Focus
    switch $D {
        next    {
            if {$S == ""} {
                if {[info exists W(ScrFrame,[expr $H + 1],Points)]} {
                    focus $W(ScrFrame,[expr $H + 1],Points)
                }
            } else {
                switch $S {
                    1   {
                        focus $W(ScrFrame,$H,2)
                    }
                    2   {
                        focus $W(ScrFrame,$H,3)
                    }
                    3   {
                        if {[info exists W(ScrFrame,[expr $H + 1],1)]} {
                            focus $W(ScrFrame,[expr $H + 1],1)
                        }
                    }
                }
            }
        }
        right   {
            if {$S == ""} {
            } else {
                switch $S {
                    1   {
                        focus $W(ScrFrame,$H,2)
                    }
                    2   {
                        focus $W(ScrFrame,$H,3)
                    }
                    3   {
                        focus $W(ScrFrame,$H,Points)
                    }
                }
            }
        }
        down    {
            if {$S == ""} {
                if {[info exists W(ScrFrame,[expr $H + 1],Points)]} {
                    focus $W(ScrFrame,[expr $H + 1],Points)
                }
            } else {
                switch $S {
                    1   {
                        if {[info exists W(ScrFrame,[expr $H + 1],1)]} {
                            focus $W(ScrFrame,[expr $H + 1],1)
                        }
                    }
                    2   {
                        if {[info exists W(ScrFrame,[expr $H + 1],2)]} {
                            focus $W(ScrFrame,[expr $H + 1],2)
                        }
                    }
                    3   {
                        if {[info exists W(ScrFrame,[expr $H + 1],3)]} {
                            focus $W(ScrFrame,[expr $H + 1],3)
                        }
                    }
                }
            }
        }
        prev    {
            if {$S == ""} {
                if {$H == 1} {
                    focus $W(HeadFrame,Rest0)
                } elseif {[info exists W(ScrFrame,[expr $H - 1],Points)]} {
                    focus $W(ScrFrame,[expr $H - 1],Points)
                }
            } else {
                switch $S {
                    1   {
                        if {$H == 1} {
                            focus $W(HeadFrame,Rest0)
                        } elseif {[info exists W(ScrFrame,[expr $H - 1],3)]} {
                            focus $W(ScrFrame,[expr $H - 1],3)
                        }
                    }
                    2   {
                        focus $W(ScrFrame,$H,1)
                    }
                    3   {
                        focus $W(ScrFrame,$H,2)
                    }
                }
            }
        }
        left   {
            if {$S == ""} {
                focus $W(ScrFrame,$H,3)
            } else {
                switch $S {
                    1   {
                    }
                    2   {
                        focus $W(ScrFrame,$H,1)
                    }
                    3   {
                        focus $W(ScrFrame,$H,2)
                    }
                }
            }
        }
         up  {
            if {$S == ""} {
                if {$H == 1} {
                    focus $W(HeadFrame,Rest0)
                } elseif {[info exists W(ScrFrame,[expr $H - 1],Points)]} {
                    focus $W(ScrFrame,[expr $H - 1],Points)
                }
            } else {
                switch $S {
                    1   {
                        if {$H == 1} {
                            focus $W(HeadFrame,Name)
                        } elseif {[info exists W(ScrFrame,[expr $H - 1],1)]} {
                            focus $W(ScrFrame,[expr $H - 1],1)
                        }
                    }
                    2   {
                        if {$H == 1} {
                            focus $W(HeadFrame,Name)
                        } elseif {[info exists W(ScrFrame,[expr $H - 1],2)]} {
                            focus $W(ScrFrame,[expr $H - 1],2)
                        }
                    }
                    3   {
                        if {$H == 1} {
                            focus $W(HeadFrame,Name)
                        } elseif {[info exists W(ScrFrame,[expr $H - 1],3)]} {
                            focus $W(ScrFrame,[expr $H - 1],3)
                        }
                    }
                }
            }
        }
    }
}

#---    New / Reset Game    --------------------------------

proc resetX01 {} {
    global W X01
    set Bckp(Rest0)  $X01(Rest0)
    set Bckp(SbarMsg)    $X01(SbarMsg)
    array unset X01
    foreach w [array names W ScrFrame,\[0-9\]*,*] {
        destroy $W($w)
        unset W($w)
    }
    set X01(Rest0)  $Bckp(Rest0)
    set X01(SbarMsg)    ""
    crHeat 1 $W(ScrFrame)
}

#------------------------------------------------------------------------------
#           Helper
#

#--- Place labels outside of gridded scrolledframe   -------

proc gridHead {} {
    global W
    lassign [grid bbox $W(ScrFrame) 0 0] x y w h
    place $W(CapFrame,Shots) -x $x -y 0 -width $w
    lassign [grid bbox $W(ScrFrame) 1 0] x y w h
    place $W(CapFrame,Sum) -x $x -y 0 -width $w
    lassign [grid bbox $W(ScrFrame) 2 0] x y w h
    place $W(CapFrame,Rest) -x $x -y 0 -width $w
    $W(CapFrame) configure -height [winfo reqheight $W(CapFrame,Rest)]
}

#==============================================================================
#           Main
#

mainGui