# main.tcl
# Copyright (c) 2003 Digi International Inc., All Rights Reserved
# 
# This software contains proprietary and confidential information of Digi
# International Inc.  By accepting transfer of this copy, Recipient agrees
# to retain this software in confidence, to prevent disclosure to others,
# and to make no use of this software other than that for which it was
# delivered.  This is an unpublished copyrighted work of Digi International
# Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
# prohibited.
# 
# Restricted Rights Legend
#
# Use, duplication, or disclosure by the Government is subject to
# restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
# Technical Data and Computer Software clause at DFARS 252.227-7031 or
# subparagraphs (c)(1) and (2) of the Commercial Computer Software -
# Restricted Rights at 48 CFR 52.227-19, as applicable.
#
# Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
#
# ***************************************************************************

set strVersion "Revision C, Build 1 - April, 2004"

##A simple splash screen... 
wm title . "Digi International"
set boolDefault [grid propagate .]
grid propagate . false
grid [label .loading -text "Loading..."] -sticky news
update
grid propagate . $boolDefault

##Force our Tcl/Tk library only
set os [lindex $::argv 0]
set dgRoot [file join [pwd] [lindex $::argv 1]]
set tclRoot [file dirname [file dirname [file dirname [info nameofexecutable]]]]
set auto_path {}
lappend auto_path [file join $tclRoot $os lib]
lappend auto_path [file join $tclRoot $os lib tcl8.3]
lappend auto_path [file join $tclRoot $os bin]
lappend auto_path [file join $tclRoot lib]
lappend auto_path [file join $tclRoot lib tcl8.3]
lappend auto_path [file join $tclRoot lib tk8.3]
lappend auto_path [file join $tclRoot lib widget]
lappend auto_path [file join $tclRoot lib tools]
lappend auto_path [file join $tclRoot lib tcllib0.8]

## For Tab order (cross-platform)
catch {event add <<ShiftTab>> <Shift-Tab> }
catch {event add <<ShiftTab>> <ISO_Left_Tab> }
catch {event add <<ShiftTab>> <ISO_Right_Tab> }

## CD version
set boolRunFromCD 1
#set boolRunFromCD 0

## Paths
set pathAddp [file join [file dirname [info nameofexecutable]] addp]
set dirImages [file join $dgRoot images]
set dirMsgs [file join $dgRoot msgs]

## Load images
image create photo imgWaterLogo -format ppm -file [file join $dirImages waterlogo256-pure128.ppm]
image create photo imgDevice -format ppm -file [file join $dirImages device.ppm]
image create photo imgBox -format ppm -file [file join $dirImages box.ppm]
image create photo imgBoxc -format ppm -file [file join $dirImages boxc.ppm]
image create photo imgBoxDisabled -format ppm -file [file join $dirImages box_disabled.ppm]
image create photo imgSearch -format ppm -file [file join $dirImages search.ppm]
image create photo imgConfig -format ppm -file [file join $dirImages config.ppm]
image create photo imgNotfound -format ppm -file [file join $dirImages notfound.ppm]
## A bullet bitmap
set dataBMP {
#define bullet_width  11
#define bullet_height 9
    static char bullet_bits[] = {
        0x00,0x00, 0x00,0x00, 0x70,0x00, 0xf8,0x00, 0xf8,0x00,
        0xf8,0x00, 0x70,0x00, 0x00,0x00, 0x00,0x00
    };
}
image create bitmap bmap -data $dataBMP

## Program variables
set lstDisplayed {}
set numSelectedDevice ""
set boolIsWireless 0
set colorSystem [. cget -background]

font create fontRegularBold -family Helvetica -size 12 -weight bold
font create fontRegular -family Helvetica -size 12 -weight normal
font create fontLink -family Helvetica -size 12 -weight normal -underline 1
set numFMeasure [font measure fontRegular 1234567890]

set numScreenWidth [winfo screenwidth .]
if {$numScreenWidth < 690} {
    set numWWidth 635   
    set numWHeight 475
    if {$numFMeasure > 70} {
        font configure fontRegular -size 8
        font configure fontRegularBold -size 8
        font configure fontLink -size 8
    }
} elseif {$numScreenWidth > 1024} {
    set numWWidth 760
    set numWHeight 530
    if {$numFMeasure > 80} {
        font configure fontRegular -size 10
        font configure fontRegularBold -size 10
        font configure fontLink -size 10
    }
} else {
    set numWWidth 690
    set numWHeight 490
    if {$numFMeasure < 65} {
        font configure fontRegular -size 13
        font configure fontRegularBold -size 13
        font configure fontLink -size 13
    } elseif {$numFMeasure > 70} {
        font configure fontRegular -size 8
        font configure fontRegularBold -size 8
        font configure fontLink -size 8
    }
}
set strGeometry ${numWWidth}x$numWHeight
set numTitleBoldXPad 15
set numTitleDetailXPad 30
set numCanvasWidth [image width imgWaterLogo]
set numCanvasHeight [image height imgWaterLogo]
set numImgDeviceWidth [image width imgDevice]
set numImgDeviceHeight [image height imgDevice]
set numTitleBoldWraplength [expr $numWWidth - $numCanvasWidth - $numImgDeviceWidth - \
 (2 * $numTitleDetailXPad) - (2 * $numTitleBoldXPad)]
set numTitleDetailWraplength [expr $numTitleBoldWraplength - 20]
set numCurPage 0
set numCurBoxButton 0
set numBorderWidth 7
set numButtonWidth 8
set numGutsHeadingWraplength [expr $numWWidth - $numCanvasWidth - (2 * $numTitleBoldXPad) - \
 (2 * $numBorderWidth) - 20]
set lstPageCommands [list PageWelcome PageDiscover PageNetwork PageWireless PageVerify PageSave PageFinish]

## Set the default font for all widgets
option add *font fontRegular

## Conform widgets to a standard look and feel across platforms.
option add *Entry*background white
option add *ComboBox*background white
option add *ArrowButton*background $colorSystem
option add *Radiobutton*HighlightBackground $colorSystem
option add *Checkbutton*HighlightBackground $colorSystem

set httpRegister http://www.digi.com/registration

## Load packages
package require BWidget
package require Tablelist
package require Mentry
package require Wcb
package require msgcat
package require Tools

## Setup message catalog
namespace import ::msgcat::*
if {![::msgcat::mcload $dirMsgs]} {
    ## No .msg file for users Local(LANG). Just use en
    mclocale en
    ::msgcat::mcload $dirMsgs
}                                 
## Look for a lang specific images folder
foreach loc [mcpreferences] {
    set dir [file join $dirImages $loc]
    if {[file isdirectory $dir]} {
        set dirImages $dir
        break
    }
}
## Not sure if this is needed.  It was used on the CD frontend.
proc ::msgcat::mcunknown {locale strSrc args}  {
    ## This mirrors the original definition.  It's repeated here for debug purposes
    if {[llength $args]} {
        return [eval [list format $strSrc] $args]
    } else {
        return $strSrc
    }
    ## End of mirror
}

## Get web browser path
set pathBrowser ""
if {$boolRunFromCD} {
    set pathBrowser [lindex $::argv 2]
} else {
    regsub -all -- {\\} [Browser::path] / pathBrowser
}

set lstFiles {welcome.tcl discover.tcl network.tcl config.tcl verify.tcl wireless.tcl \
 save.tcl finish.tcl fail.tcl window.tcl}
foreach file $lstFiles {
    source [file join $dgRoot tcl $file]
}
## END INIT ##

## Button detail
proc Hotbutton {w args} {
    return [eval button $w $args -width $::numButtonWidth]
}

proc ShowHelp {{strAnchor {}}} {
    if {[string equal $strAnchor ""]} {
        switch $::numCurPage {
            1 { set strAnchor #Discover }
            2 { set strAnchor #Network }
            3 { set strAnchor #Wireless }
            6 { set strAnchor #Troubleshooting }
            default {set strAnchor ""}
        }
    }
    ViewLocalHTML [file join $::dgRoot help.htm$strAnchor]
}

proc _encode {str} {
    
    regsub -all {\+} $str {%2b} str
    regsub -all {&} $str {%26} str
    regsub -all {=} $str {%3d} str
    regsub -all {\"} $str {%22} str
    regsub -all {#} $str {%23} str
    regsub -all {;} $str {%3b} str
    regsub -all {\?} $str {%3f} str
#   regsub -all {:} $str {%3a} str
    regsub -all {\$} $str {%24} str
    regsub -all {!} $str {%21} str
    regsub -all {,} $str {%2c} str
    regsub -all {'} $str {%27} str
    regsub -all {\(} $str {%28} str
    regsub -all {\)} $str {%29} str
    regsub -all { } $str {%20} str

    return $str
}

proc ViewLocalHTML {fileHTML} {
    if {[string first nets [string tolower $::pathBrowser]] != -1} {
        ## any version of netscape
        set strFile "file:"
        if {$::tcl_platform(platform) == "windows"} {
            append strFile /
        }
        if {[string first netscp6 [string tolower $::pathBrowser]] != -1} {
            ## netscape version 6
            set encode 0
        } else {
            ## previous version of netscape
            set encode 1
        }                               
    } else {
        ## not netscape
        set strFile "file:"
        set encode 1
    }
    if {$encode} {
        set strPath [_encode $fileHTML]
    }
    catch [list exec -- $::pathBrowser "$strFile$fileHTML" &] 
}

proc ButtonPress {strAction} {
    switch $strAction {
        back { PrevPage }
        next { NextPage }
        cancel { destroy .; exit 0 }
        help { ShowHelp }
        finish {
            if {$::Finish::boolRegister} {
                catch [list exec -- $::pathBrowser $::httpRegister &]
            }
            if {$::Finish::boolLogon} {
                catch [list exec -- $::pathBrowser http://$::Save::arrSettings(IP) &]
            }
            ButtonPress cancel
        }
        reset {
            UpdateButtons rediscover
            ButtonPress back
            ::Discover::RunAddp
            $::btnBack configure -command {ButtonPress back}
        }
        start {
            foreach child [winfo children $::frmGuts] {
                destroy $child
            }
            PageWelcome
            UpdateButtons start
        }
        skip {
            set ::boolIsWireless 0
            NextPage
        }
    }
}
proc NextPage {} {
    eval [lindex $::lstPageCommands [expr $::numCurPage + 1]] next
}

proc PrevPage {} {
    eval [lindex $::lstPageCommands [expr $::numCurPage - 1]] back
}

proc PageWelcome {{strDirection {}}} {
    set ::stateDiscover stop
    Welcome::MainGuts $::frmGuts
    UpdateButtons welcome
    UpdateTitle welcome
    foreach box $::lstBoxIDs {
        $::c itemconfigure $box -image imgBox
    }
    $::c itemconfigure [lindex $::lstBoxIDs 0] -image imgBoxc
    set ::numCurPage 0
    set ::numCurBoxButton 0
}
proc PageDiscover {strDirection} {
    Discover::MainGuts $::frmGuts
    UpdateTitle discover
    UpdateBoxButton $strDirection
    UpdateCurPage $strDirection
    if {$strDirection == "next"} {
        UpdateButtons prediscover
        Discover::RunAddp
    } else {
        ## The table widget loses its selection when clicking on entry boxes
        $::tblWidget selection set $::numSelectedDevice
        UpdateButtons discover
        array set ::Discover::arrSettings {}
    }
}
proc PageNetwork {strDirection} {
    if {$strDirection == "next"} {
        ## The table widget loses its selection when clicking on entry boxes
        set ::numSelectedDevice [$::tblWidget curselection]
        if {![string equal $::numSelectedDevice ""]} {
            set lstDevice [lindex $::lstDisplayed $::numSelectedDevice]
        } else {
            set lstDevice noneselected
        }
        if {[Network::IsConfigurable $lstDevice]} {
            set strMAC [lindex $lstDevice 1]
            Discover::InitSettings
            if {![Discover::GetSettings $strMAC]} {
                set ::Discover::arrSettings(MAC) $strMAC
                set ::Discover::arrSettings(Hardware) [lindex $lstDevice 3]
                set ::Fail::strType getsettingsfail
                PageFail $strDirection
            } else {
                Config::Init
                Config::NetPreset
                Network::MainGuts $::frmGuts 
                Network::DHCPState
                UpdateTitle network [Network::GetHardware $lstDevice]
                UpdateBoxButton $strDirection
                UpdateCurPage $strDirection
            }
        }
    } else {
        set lstDevice [lindex $::lstDisplayed $::numSelectedDevice]
        Network::MainGuts $::frmGuts 
        UpdateTitle network [Network::GetHardware $lstDevice]
        UpdateCurPage $strDirection
        UpdateButtons network
    }
}
proc PageWireless {strDirection} {
    if {$strDirection == "next"} {
        if {[info exists ::Discover::arrSettings(AutoSSID)]} {
            set ::boolIsWireless 1
            Config::WlessPreset
            Wireless::MainGuts $::frmGuts
            ## Disable(gray out) entries based on other values
            Wireless::SSIDState
            Wireless::SecurityState
       
            UpdateTitle wireless
            UpdateCurPage $strDirection
            UpdateButtons wireless
        } else {
            set ::boolIsWireless 0
            UpdateCurPage $strDirection
            NextPage
        }
    } elseif {[info exists ::Discover::arrSettings(AutoSSID)]} {
        set ::boolIsWireless 1
        Wireless::MainGuts $::frmGuts
        UpdateTitle wireless
        UpdateCurPage $strDirection
        UpdateButtons wireless
    } else {
        UpdateCurPage $strDirection
        PrevPage
    }
}
proc PageVerify {strDirection} {
    if {$strDirection == "next" && $::boolIsWireless} {
        if {$::Wireless::boolShowKey && ![::Wireless::VerifyKey]} {
            tk_messageBox -title [mc wireless.missingkey.title] -parent . \
             -message [mc wireless.missingkey.msg] -icon warning -type ok 
            return
        }
    }
    Verify::MainGuts $::frmGuts
    UpdateTitle verify
    if {$strDirection == "back"} {
        UpdateBoxButton $strDirection
    } else {
        Config::Arrays2Strings
        Verify::ShowSettings
    }
    UpdateCurPage $strDirection
    UpdateButtons verify
}
proc PageSave {strDirection} {
    UpdateBoxButton $strDirection
    UpdateCurPage $strDirection
    if {$strDirection == "back"} {
        PrevPage
    } else {
        UpdateButtons save
        UpdateTitle save $::Discover::arrSettings(Hardware)
        if {[Save::MainGuts $::frmGuts]} {
            NextPage
        } else {
            PageFail next
        }
    }
}
proc PageFinish {strDirection} {
    UpdateButtons finish
    UpdateTitle finish
    UpdateBoxButton $strDirection
    UpdateCurPage $strDirection
    Finish::MainGuts $::frmGuts
}

proc PageFail {strDirection} {
    switch $::Fail::strType {
        getsettingsfail {
            set ::Fail::lstArgs [list $::Discover::arrSettings(MAC)]
            $::btnBack configure -command {ButtonPress reset}
            UpdateButtons $::Fail::strType
            UpdateTitle $::Fail::strType $::Discover::arrSettings(Hardware)
            UpdateBoxButton $strDirection
            UpdateCurPage $strDirection
            Fail::MainGuts $::frmGuts
        }
        newpassword {
            if {[Save::MainGuts $::frmGuts]} {
                NextPage
            } else {
                PageFail next
            }
        }
        default {
            UpdateButtons fail
            UpdateTitle fail $::Discover::arrSettings(Hardware)
            UpdateBoxButton $strDirection
            UpdateCurPage $strDirection
            Fail::MainGuts $::frmGuts
        }
    }
}

proc UpdateButtons {strPage} {
    switch $strPage {
        welcome {
            $::btnBack configure -state disabled
            $::btnStart configure -state disabled
            $::btnSkip configure -state disabled
        }
        prediscover {
            $::btnBack configure -state normal
            $::btnNext configure -state disabled
        }
        discover {
            $::btnBack configure -state normal
        }
        verify {
            UnassignButton $::btnSkip [mc button.skip]
            $::btnSkip configure -state disabled
            UnassignButton $::btnNext [mc button.finish]
            AssignButton $::btnNext [mc button.next]
            $::btnNext configure -command {ButtonPress next}
        }
        save {
            foreach w [list $::btnBack $::btnNext $::btnCancel $::btnHelp] {
                $w configure -state disabled
            }
        }
        finish {
            foreach w [list $::btnBack $::btnNext $::btnCancel $::btnHelp] {
                $w configure -state normal
            }
            UnassignButton $::btnNext [mc button.next]
            AssignButton $::btnNext [mc button.finish]
            $::btnNext configure -command {ButtonPress finish}
            AssignButton $::btnStart [mc button.start]
            $::btnStart configure -state normal
        }
        getsettingsfail -
        fail {
            foreach w [list $::btnBack $::btnNext $::btnCancel $::btnHelp] {
                $w configure -state normal
            }
            UnassignButton $::btnNext [mc button.next]
            AssignButton $::btnNext [mc button.finish]
            $::btnNext configure -command {ButtonPress cancel}
            AssignButton $::btnStart [mc button.start]
            $::btnStart configure -state normal
        }
        rediscover {
            UnassignButton $::btnNext [mc button.finish]
            AssignButton $::btnNext [mc button.next]
            $::btnNext configure -command {ButtonPress next}
        }
        start {
            UnassignButton $::btnNext [mc button.start]
            UnassignButton $::btnNext [mc button.skip]
            UnassignButton $::btnNext [mc button.finish]
            AssignButton $::btnNext [mc button.next]
            $::btnNext configure -command {ButtonPress next}
            UpdateButtons welcome
        }
        network {
            UnassignButton $::btnSkip [mc button.skip]
            $::btnSkip configure -state disabled
        }
        wireless {
            AssignButton $::btnSkip [mc button.skip]
            $::btnSkip configure -state normal
        }
    }
}

proc UpdateTitle {strPage args} {
    set ::strTitleBold [mc $strPage.title.bold]
    ## This format command only works for one specifier
    set ::strTitleDetail [mc $strPage.title.detail [join $args]]
}

proc UpdateCurPage {strDirection} {
    if {$strDirection == "next"} {
        incr ::numCurPage   
    } else {
        incr ::numCurPage -1
    }
}

proc UpdateBoxButton {strDirection} {
    if {$strDirection == "next"} {
        $::c itemconfigure [lindex $::lstBoxIDs $::numCurBoxButton] -image imgBoxDisabled
        incr ::numCurBoxButton
        $::c itemconfigure [lindex $::lstBoxIDs $::numCurBoxButton] -image imgBoxc
    } else {
        $::c itemconfigure [lindex $::lstBoxIDs $::numCurBoxButton] -image imgBox
        incr ::numCurBoxButton -1
        $::c itemconfigure [lindex $::lstBoxIDs $::numCurBoxButton] -image imgBoxc
    }
}

proc UnassignButton {w strUText} {
    ## If a button is given new text we disable the old hotkey
    set numAmp [string first & $strUText]
    set strText [string map {& ""} $strUText]
    set strChar [string index $strText $numAmp]
    bind $::frmButtons <[string toupper $strChar]> {}
    bind $::frmButtons <[string tolower $strChar]> {}
    bind . <Alt_L><[string toupper $strChar]> {}
    bind . <Alt_L><[string tolower $strChar]> {}
}

proc AssignButton {w strUText} {
    set numAmp [string first & $strUText]
    set strText [string map {& ""} $strUText]
    set strChar [string index $strText $numAmp]
    
    ## No need for Alt key when button frame has focus
    bind $::frmButtons <[string toupper $strChar]> [list tkButtonInvoke $w]
    bind $::frmButtons <[string tolower $strChar]> [list tkButtonInvoke $w]
    
    bind . <Alt_L><[string toupper $strChar]> [list tkButtonInvoke $w ; focus $w]
    bind . <Alt_L><[string tolower $strChar]> [list tkButtonInvoke $w ; focus $w]
    
    SetButtonBinding $w
    $w configure -text $strText -underline $numAmp
}

proc SetButtonBinding {w} {
    ## Add the button frame bindings 
    bindtags $w [list $w $::frmButtons Button . all]
}

proc LabelHeading {w strText} {
    set lblHeading [label $w -text $strText \
     -wraplength $::numGutsHeadingWraplength -justify left -font fontRegular -borderwidth 0]
}

proc wheelEvent { x y delta } {
    ## Find out what's the widget we're on
    set act 0
    set widget [winfo containing $x $y]

    if {$widget != ""} {
        ## Make sure we've got a vertical scrollbar for this widget
        if {[catch "$widget cget -yscrollcommand" cmd]} return

        if {$cmd != ""} {
            ## Find out the scrollbar widget we're using
            set scroller [lindex $cmd 0]

            ## Make sure we act
            set act 1
        }
    }

    if {$act == 1} {
        ## Now we know we have to process the wheel mouse event
        set xy [$widget yview]
        set factor [expr [lindex $xy 1]-[lindex $xy 0]]

        ## Make sure we activate the scrollbar's command
        set cmd "[$scroller cget -command] scroll [expr -int($delta/(60*$factor))] units"
        eval $cmd
    }
}

proc MainGetSettings {strMAC arrNAME} {
    upvar #0 $arrNAME arr
    array unset $arrNAME
    set lstAddp [exec -- $::pathAddp -q -m $strMAC]
    set lstAddp [split $lstAddp \n]
    if {[llength $lstAddp] < 2} {
        return 0
    }
    foreach strSetting $lstAddp {
        if {[regexp {([\w]*) *: (.*)} $strSetting matchVar sub1 sub2]} {
            set arr($sub1) $sub2
        }
    }
    foreach name {DHCP} {
        if {![info exists arr($name)]} {
            set arr($name) NA
        }
    }
#parray $arrNAME
    return 1
}

proc DebugConsole {{strTitle "Debug Console"}} {
    set d .debugconsole
    ## Bug workaround with mentry
    catch {namespace import ::mentry::*}
    if {![winfo exists $d]} {
        toplevel $d
        ::Console $d.con
        grid $d.con
    }
    wm title $d $strTitle
    raise $d
}


## Main Program
wm geometry . $strGeometry
::window::center .
wm title . [mc wizard.title]
wm resizable . 1 1
bind . <Escape> {ButtonPress cancel}
bind . <F1> {ButtonPress help}
bind . <Control-F1> {DebugConsole}

set c [canvas .c -width $numCanvasWidth -height $numCanvasHeight]
set frmMain [frame .frmmain]
set frmBottom [frame .frmbottom]
grid $c -row 0 -column 0
grid $frmMain -row 0 -column 1 -sticky news
grid $frmBottom -row 1 -column 0 -columnspan 2 -sticky news
grid columnconfigure . 1 -weight 1
grid rowconfigure . 0 -weight 1

$c create image 1 1 -anchor nw -image imgWaterLogo
set numX 17
set numY 147
set numYIncr 26
set numXTextOffset 24
set numYTextOffset 2
proc BoxItem {c strText} {
    set boxItem [$c create image $::numX $::numY -anchor nw -image imgBox]
    $c create text [expr $::numX + $::numXTextOffset] [expr $::numY + $::numYTextOffset] \
     -anchor nw -text $strText -tags tagBox
    incr ::numY $::numYIncr
    return $boxItem
}
set lstBoxItems [list [mc box.welcome] \
 [mc box.discover] \
 [mc box.configure] \
 [mc box.save] \
 [mc box.finish]]

set lstBoxIDs {}
foreach item $lstBoxItems {
    lappend lstBoxIDs [BoxItem $c $item]
}

$c itemconfigure tagBox -fill white -font fontRegularBold 
$c itemconfigure [lindex $lstBoxIDs 0] -image imgBoxc

set frmTitle [frame $frmMain.frmtitle -bg white -borderwidth $numBorderWidth]
set sepTitle [Separator $frmMain.sep -orient horizontal]
set frmGuts [frame $frmMain.frmguts]
grid $frmTitle -row 0 -column 0 -sticky new
grid $sepTitle -row 1 -column 0 -sticky new
grid $frmGuts -row 2 -column 0 -sticky news
grid columnconfigure $frmMain 0 -weight 1

# Allow subframes to fill the guts
grid columnconfigure $frmGuts 0 -weight 1
grid rowconfigure $frmGuts 0 -weight 1

## Push frmTitle to the top with frmGuts
grid rowconfigure $frmMain 2 -weight 1

set sepBottom [Separator $frmBottom.sep -orient horizontal]
set frmButtons [frame $frmBottom.frmbuttons -borderwidth $numBorderWidth]
grid $sepBottom -row 0 -column 0 -sticky new
grid $frmButtons -row 1 -column 0 -sticky news
grid columnconfigure $frmBottom 0 -weight 1
## Push sepBottom to the top with frmButtons
grid rowconfigure $frmBottom 1 -weight 1

## Title detail
set lblTitleBold [label $frmTitle.lblbold -text [mc welcome.title.bold] -bg white \
 -wraplength $numTitleBoldWraplength -justify left -font fontRegularBold -borderwidth 0 \
 -textvariable ::strTitleBold]
set lblTitleDetail [label $frmTitle.lbldetail -text [mc welcome.title.detail] -bg white \
 -wraplength $numTitleDetailWraplength -justify left -font fontRegular -borderwidth 0 \
 -textvariable ::strTitleDetail]
set lblDevice [label $frmTitle.lbldevice -image imgDevice -borderwidth 0]
grid $lblTitleBold -row 0 -column 0 -padx $numTitleBoldXPad -sticky nw
grid $lblTitleDetail -row 1 -column 0 -padx $numTitleDetailXPad -sticky nw
grid $lblDevice -row 0 -column 1 -sticky e -rowspan 2
grid columnconfigure $frmTitle 1 -weight 1
grid rowconfigure $frmTitle 1 -weight 2

## Navigation buttons
set btnNext [Hotbutton $frmButtons.btnnext -command {ButtonPress next}]
set btnCancel [Hotbutton $frmButtons.btncancel -text [mc button.cancel] -command {ButtonPress cancel}]
set btnHelp [Hotbutton $frmButtons.btnhelp -text [mc button.help] -command {ButtonPress help}]
set btnBack [Hotbutton $frmButtons.btnback -command {ButtonPress back} \
 -state disabled]
set btnStart [Hotbutton $frmButtons.btnstart -command {ButtonPress start}]
set btnSkip [Hotbutton $frmButtons.btnskip -command {ButtonPress skip}]
AssignButton $btnBack [mc button.back]
AssignButton $btnNext [mc button.next]
AssignButton $btnStart [mc button.start]
AssignButton $btnSkip [mc button.skip]
SetButtonBinding $btnCancel
SetButtonBinding $btnHelp

grid $btnStart -row 0 -column 0 -sticky e
grid $btnNext -row 0 -column 2 -sticky e
grid $btnBack -row 0 -column 1 -sticky e
grid $btnSkip -row 0 -column 3 -sticky e
grid $btnCancel -padx 5 -row 0 -column 4 -sticky e
grid $btnHelp -row 0 -column 5 -sticky e
grid columnconfigure $frmButtons 0 -weight 1

## Tweak the widget focus order
set lstButtons [list $btnStart $btnBack $btnNext $btnSkip $btnCancel $btnHelp]
foreach w $lstButtons {raise $w}
set ::Tab1st $btnNext
catch {bind $::btnHelp <Tab> {focus $::Tab1st;break}}

PageWelcome
focus $btnNext

