# ------------------------------------------------------------------------------
#  widget.tcl
#  This file is part of Unifix BWidget Toolkit
#  $Id: widget.tcl,v 1.1 2002/01/24 19:25:32 radim Exp $
# ------------------------------------------------------------------------------
#  Index of commands:
#     - Widget::tkinclude
#     - Widget::bwinclude
#     - Widget::declare
#     - Widget::addmap
#     - Widget::init
#     - Widget::destroy
#     - Widget::setoption
#     - Widget::configure
#     - Widget::cget
#     - Widget::subcget
#     - Widget::hasChanged
#     - Widget::_get_tkwidget_options
#     - Widget::_test_tkresource
#     - Widget::_test_bwresource
#     - Widget::_test_synonym
#     - Widget::_test_string
#     - Widget::_test_flag
#     - Widget::_test_enum
#     - Widget::_test_int
#     - Widget::_test_boolean
# ------------------------------------------------------------------------------

namespace eval Widget {
    variable _optiontype
    variable _class
    variable _tk_widget

    array set _optiontype {
        TkResource Widget::_test_tkresource
        BwResource Widget::_test_bwresource
        Enum       Widget::_test_enum
        Int        Widget::_test_int
        Boolean    Widget::_test_boolean
        String     Widget::_test_string
        Flag       Widget::_test_flag
        Synonym    Widget::_test_synonym
    }

    proc use {} {}
}



# ------------------------------------------------------------------------------
#  Command Widget::tkinclude
#     Includes tk widget resources to BWidget widget.
#  class      class name of the BWidget
#  tkwidget   tk widget to include
#  subpath    subpath to configure
#  args       additionnal args for included options
# ------------------------------------------------------------------------------
proc Widget::tkinclude { class tkwidget subpath args } {
    foreach {cmd lopt} $args {
        # cmd can be
        #   include      options to include            lopt = {opt ...}
        #   remove       options to remove             lopt = {opt ...}
        #   rename       options to rename             lopt = {opt newopt ...}
        #   prefix       options to prefix             lopt = {prefix opt opt ...}
        #   initialize   set default value for options lopt = {opt value ...}
        #   readonly     set readonly flag for options lopt = {opt flag ...}
        switch -- $cmd {
            remove {
                foreach option $lopt {
                    set remove($option) 1
                }
            }
            include {
                foreach option $lopt {
                    set include($option) 1
                }
            }
            prefix {
                set prefix [lindex $lopt 0]
                foreach option [lrange $lopt 1 end] {
                    set rename($option) "-$prefix[string range $option 1 end]"
                }
            }
            rename     -
            readonly   -
            initialize {
                array set $cmd $lopt
            }
            default {
                return -code error "invalid argument \"$cmd\""
            }
        }
    }

    namespace eval $class {}
    upvar 0 ${class}::opt classopt
    upvar 0 ${class}::map classmap

    # create resources informations from tk widget resources
    foreach optdesc [_get_tkwidget_options $tkwidget] {
        set option [lindex $optdesc 0]
        if { (![info exists include] || [info exists include($option)]) &&
             ![info exists remove($option)] } {
            if { [llength $optdesc] == 3 } {
                # option is a synonym
                set syn [lindex $optdesc 1]
                if { ![info exists remove($syn)] } {
                    # original option is not removed
                    if { [info exists rename($syn)] } {
                        set classopt($option) [list Synonym $rename($syn)]
                    } else {
                        set classopt($option) [list Synonym $syn]
                    }
                }
            } else {
                if { [info exists rename($option)] } {
                    set realopt $option
                    set option  $rename($option)
                } else {
                    set realopt $option
                }
                if { [info exists initialize($option)] } {
                    set value $initialize($option)
                } else {
                    set value [lindex $optdesc 1]
                }
                if { [info exists readonly($option)] } {
                    set ro $readonly($option)
                } else {
                    set ro 0
                }
                set classopt($option) [list TkResource $value $ro [list $tkwidget $realopt]]
                lappend classmap($option) $subpath "" $realopt
            }
        }
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::bwinclude
#     Includes BWidget resources to BWidget widget.
#  class    class name of the BWidget
#  subclass BWidget class to include
#  subpath  subpath to configure
#  args     additionnal args for included options
# ------------------------------------------------------------------------------
proc Widget::bwinclude { class subclass subpath args } {
    foreach {cmd lopt} $args {
        # cmd can be
        #   include      options to include            lopt = {opt ...}
        #   remove       options to remove             lopt = {opt ...}
        #   rename       options to rename             lopt = {opt newopt ...}
        #   prefix       options to prefix             lopt = {prefix opt opt ...}
        #   initialize   set default value for options lopt = {opt value ...}
        #   readonly     set readonly flag for options lopt = {opt flag ...}
        switch -- $cmd {
            remove {
                foreach option $lopt {
                    set remove($option) 1
                }
            }
            include {
                foreach option $lopt {
                    set include($option) 1
                }
            }
            prefix {
                set prefix [lindex $lopt 0]
                foreach option [lrange $lopt 1 end] {
                    set rename($option) "-$prefix[string range $option 1 end]"
                }
            }
            rename     -
            readonly   -
            initialize {
                array set $cmd $lopt
            }
            default {
                return -code error "invalid argument \"$cmd\""
            }
        }
    }

    namespace eval $class {}
    upvar 0 ${class}::opt classopt
    upvar 0 ${class}::map classmap
    upvar 0 ${subclass}::opt subclassopt

    # create resources informations from BWidget resources
    foreach {option optdesc} [array get subclassopt] {
        if { (![info exists include] || [info exists include($option)]) &&
             ![info exists remove($option)] } {
            set type [lindex $optdesc 0]
            if { ![string compare $type "Synonym"] } {
                # option is a synonym
                set syn [lindex $optdesc 1]
                if { ![info exists remove($syn)] } {
                    if { [info exists rename($syn)] } {
                        set classopt($option) [list Synonym $rename($syn)]
                    } else {
                        set classopt($option) [list Synonym $syn]
                    }
                }
            } else {
                if { [info exists rename($option)] } {
                    set realopt $option
                    set option  $rename($option)
                } else {
                    set realopt $option
                }
                if { [info exists initialize($option)] } {
                    set value $initialize($option)
                } else {
                    set value [lindex $optdesc 1]
                }
                if { [info exists readonly($option)] } {
                    set ro $readonly($option)
                } else {
                    set ro [lindex $optdesc 2]
                }
                set classopt($option) [list $type $value $ro [lindex $optdesc 3]]
                lappend classmap($option) $subpath $subclass $realopt
            }
        }
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::declare
#    Declares new options to BWidget class.
# ------------------------------------------------------------------------------
proc Widget::declare { class optlist } {
    variable _optiontype

    namespace eval $class {}
    upvar 0 ${class}::opt classopt

    foreach optdesc $optlist {
        set option  [lindex $optdesc 0]
        set optdesc [lrange $optdesc 1 end]
        set type    [lindex $optdesc 0]

        if { ![info exists _optiontype($type)] } {
            # invalid resource type
            return -code error "invalid option type \"$type\""
        }

        if { ![string compare $type "Synonym"] } {
            # test existence of synonym option
            set syn [lindex $optdesc 1]
            if { ![info exists classopt($syn)] } {
                return -code error "unknow option \"$syn\" for Synonym \"$option\""
            }
            set classopt($option) [list Synonym $syn]
            continue
        }

        # all other resource may have default value, readonly flag and
        # optional arg depending on type
        set value [lindex $optdesc 1]
        set ro    [lindex $optdesc 2]
        set arg   [lindex $optdesc 3]

        if { ![string compare $type "BwResource"] } {
            # We don't keep BwResource. We simplify to type of sub BWidget
            set subclass    [lindex $arg 0]
            set realopt     [lindex $arg 1]
            if { ![string length $realopt] } {
                set realopt $option
            }

            upvar 0 ${subclass}::opt subclassopt
            if { ![info exists subclassopt($realopt)] } {
                return -code error "unknow option \"$realopt\""
            }
            set suboptdesc $subclassopt($realopt)
            if { $value == "" } {
                # We initialize default value
                set value [lindex $suboptdesc 1]
            }
            set type [lindex $suboptdesc 0]
            set ro   [lindex $suboptdesc 2]
            set arg  [lindex $suboptdesc 3]
            set classopt($option) [list $type $value $ro $arg]
            continue
        }

        # retreive default value for TkResource
        if { ![string compare $type "TkResource"] } {
            set tkwidget [lindex $arg 0]
            set realopt  [lindex $arg 1]
            if { ![string length $realopt] } {
                set realopt $option
            }
            set tkoptions [_get_tkwidget_options $tkwidget]
            if { ![string length $value] } {
                # We initialize default value
                set value [lindex [lindex $tkoptions [lsearch $tkoptions [list $realopt *]]] end]
            }
            set classopt($option) [list TkResource $value $ro [list $tkwidget $realopt]]
            continue
        }

        # for any other resource type, we keep original optdesc
        set classopt($option) [list $type $value $ro $arg]
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::addmap
# ------------------------------------------------------------------------------
proc Widget::addmap { class subclass subpath options } {
    upvar 0 ${class}::map classmap

    foreach {option realopt} $options {
        if { ![string length $realopt] } {
            set realopt $option
        }
        lappend classmap($option) $subpath $subclass $realopt
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::syncoptions
# ------------------------------------------------------------------------------
proc Widget::syncoptions { class subclass subpath options } {
    upvar 0 ${class}::sync classync

    foreach {option realopt} $options {
        if { ![string length $realopt] } {
            set realopt $option
        }
        set classync($option) [list $subpath $subclass $realopt]
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::init
# ------------------------------------------------------------------------------
proc Widget::init { class path options } {
    variable _class
    variable _optiontype

    upvar 0 ${class}::opt classopt
    upvar 0 ${class}::map classmap
    upvar 0 ${class}::$path:opt  pathopt
    upvar 0 ${class}::$path:mod  pathmod

    catch {unset pathopt}
    catch {unset pathmod}
    set fpath ".#BWidgetClass#$class"
    regsub -all "::" $class "" rdbclass
    if { ![winfo exists $fpath] } {
        frame $fpath -class $rdbclass
    }
    foreach {option optdesc} [array get classopt] {
        set type [lindex $optdesc 0]
        if { ![string compare $type "Synonym"] } {
            set option  [lindex $optdesc 1]
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
        }
        if { ![string compare $type "TkResource"] } {
            set alt [lindex [lindex $optdesc 3] 1]
        } else {
            set alt ""
        }
        set optdb [lindex [_configure_option $option $alt] 0]
        set def   [option get $fpath $optdb $rdbclass]
        if { [string length $def] } {
            set pathopt($option) $def
        } else {
            set pathopt($option) [lindex $optdesc 1]
        }
        set pathmod($option) 0
    }

    set _class($path) $class
    foreach {option value} $options {
        if { ![info exists classopt($option)] } {
            unset pathopt
            unset pathmod
            return -code error "unknown option \"$option\""
        }
        set optdesc $classopt($option)
        set type    [lindex $optdesc 0]
        if { ![string compare $type "Synonym"] } {
            set option  [lindex $optdesc 1]
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
        }
        set pathopt($option) [$_optiontype($type) $option $value [lindex $optdesc 3]]
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::destroy
# ------------------------------------------------------------------------------
proc Widget::destroy { path } {
    variable _class

    set class $_class($path)
    upvar 0 ${class}::$path:opt pathopt
    upvar 0 ${class}::$path:mod pathmod

    catch {unset pathopt}
    catch {unset pathmod}
}


# ------------------------------------------------------------------------------
#  Command Widget::configure
# ------------------------------------------------------------------------------
proc Widget::configure { path options } {
    set len [llength $options]
    if { $len <= 1 } {
        return [_get_configure $path $options]
    } elseif { $len % 2 == 1 } {
        return -code error "incorrect number of arguments"
    }

    variable _class
    variable _optiontype

    set class $_class($path)
    upvar 0 ${class}::opt  classopt
    upvar 0 ${class}::map  classmap
    upvar 0 ${class}::$path:opt pathopt
    upvar 0 ${class}::$path:mod pathmod

    set window [_get_window $class $path]
    foreach {option value} $options {
        if { ![info exists classopt($option)] } {
            return -code error "unknown option \"$option\""
        }
        set optdesc $classopt($option)
        set type    [lindex $optdesc 0]
        if { ![string compare $type "Synonym"] } {
            set option  [lindex $optdesc 1]
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
        }
        if { ![lindex $optdesc 2] } {
            set curval $pathopt($option)
            set newval [$_optiontype($type) $option $value [lindex $optdesc 3]]
            if { [info exists classmap($option)] } {
                foreach {subpath subclass realopt} $classmap($option) {
                    if { [string length $subclass] } {
                        ${subclass}::configure $window$subpath $realopt $newval
                    } else {
                        $window$subpath configure $realopt $newval
                    }
                }
            }
            set pathopt($option) $newval
            set pathmod($option) [expr {[string compare $newval $curval] != 0}]
        }
    }

    return {}
}


# ------------------------------------------------------------------------------
#  Command Widget::cget
# ------------------------------------------------------------------------------
proc Widget::cget { path option } {
    variable _class

    if { ![info exists _class($path)] } {
        return -code error "unknown widget $path"
    }

    set class $_class($path)
    upvar 0 ${class}::opt  classopt
    upvar 0 ${class}::sync classync
    upvar 0 ${class}::$path:opt pathopt

    if { ![info exists classopt($option)] } {
        return -code error "unknown option \"$option\""
    }
    set optdesc $classopt($option)
    set type    [lindex $optdesc 0]
    if { ![string compare $type "Synonym"] } {
        set option [lindex $optdesc 1]
    }

    if { [info exists classync($option)] } {
        set window [_get_window $class $path]
        foreach {subpath subclass realopt} $classync($option) {
            if { [string length $subclass] } {
                set pathopt($option) [${subclass}::cget $window$subpath $realopt]
            } else {
                set pathopt($option) [$window$subpath cget $realopt]
            }
        }
    }

    return $pathopt($option)
}


# ------------------------------------------------------------------------------
#  Command Widget::subcget
# ------------------------------------------------------------------------------
proc Widget::subcget { path subwidget } {
    variable _class

    set class $_class($path)
    upvar 0 ${class}::map classmap
    upvar 0 ${class}::$path:opt pathopt

    set result {}
    foreach {option map} [array get classmap] {
        foreach {subpath subclass realopt} $map {
            if { ![string compare $subpath $subwidget] } {
                lappend result $realopt $pathopt($option)
            }
        }
    }
    return $result
}


# ------------------------------------------------------------------------------
#  Command Widget::hasChanged
# ------------------------------------------------------------------------------
proc Widget::hasChanged { path option pvalue } {
    upvar    $pvalue value
    variable _class

    set class $_class($path)
    upvar 0 ${class}::$path:opt pathopt
    upvar 0 ${class}::$path:mod pathmod

    set value   $pathopt($option)
    set result  $pathmod($option)
    set pathmod($option) 0

    return $result
}


# ------------------------------------------------------------------------------
#  Command Widget::setoption
# ------------------------------------------------------------------------------
proc Widget::setoption { path option value } {
    variable _class

    set class $_class($path)
    upvar 0 ${class}::$path:opt pathopt

    set pathopt($option) $value
}


# ------------------------------------------------------------------------------
#  Command Widget::getoption
# ------------------------------------------------------------------------------
proc Widget::getoption { path option } {
    variable _class

    set class $_class($path)
    upvar 0 ${class}::$path:opt pathopt

    return $pathopt($option)
}


# ------------------------------------------------------------------------------
#  Command Widget::_get_window
#  returns the window corresponding to widget path
# ------------------------------------------------------------------------------
proc Widget::_get_window { class path } {
    set idx [string last "#" $path]
    if { $idx != -1 && ![string compare [string range $path [expr {$idx+1}] end] $class] } {
        return [string range $path 0 [expr {$idx-1}]]
    } else {
        return $path
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::_get_configure
#  returns the configuration list of options
#  (as tk widget do - [$w configure ?option?])
# ------------------------------------------------------------------------------
proc Widget::_get_configure { path options } {
    variable _class

    set class $_class($path)
    upvar 0 ${class}::opt classopt
    upvar 0 ${class}::map classmap
    upvar 0 ${class}::$path:opt pathopt
    upvar 0 ${class}::$path:mod pathmod

    set len [llength $options]
    if { !$len } {
        set result {}
        foreach option [lsort [array names classopt]] {
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
            if { ![string compare $type "Synonym"] } {
                set syn     $option
                set option  [lindex $optdesc 1]
                set optdesc $classopt($option)
                set type    [lindex $optdesc 0]
            } else {
                set syn ""
            }
            if { ![string compare $type "TkResource"] } {
                set alt [lindex [lindex $optdesc 3] 1]
            } else {
                set alt ""
            }
            set res [_configure_option $option $alt]
            if { $syn == "" } {
                lappend result [concat $option $res [list [lindex $optdesc 1]] [list [cget $path $option]]]
            } else {
                lappend result [list $syn [lindex $res 0]]
            }
        }
        return $result
    } elseif { $len == 1 } {
        set option  [lindex $options 0]
        if { ![info exists classopt($option)] } {
            return -code error "unknown option \"$option\""
        }
        set optdesc $classopt($option)
        set type    [lindex $optdesc 0]
        if { ![string compare $type "Synonym"] } {
            set option  [lindex $optdesc 1]
            set optdesc $classopt($option)
            set type    [lindex $optdesc 0]
        }
        if { ![string compare $type "TkResource"] } {
            set alt [lindex [lindex $optdesc 3] 1]
        } else {
            set alt ""
        }
        set res [_configure_option $option $alt]
        return [concat $option $res [list [lindex $optdesc 1]] [list [cget $path $option]]]
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::_configure_option
# ------------------------------------------------------------------------------
proc Widget::_configure_option { option altopt } {
    variable _optiondb
    variable _optionclass

    if { [info exists _optiondb($option)] } {
        set optdb $_optiondb($option)
    } else {
        set optdb [string range $option 1 end]
    }
    if { [info exists _optionclass($option)] } {
        set optclass $_optionclass($option)
    } elseif { [string length $altopt] } {
        if { [info exists _optionclass($altopt)] } {
            set optclass $_optionclass($altopt)
        } else {
            set optclass [string range $altopt 1 end]
        }
    } else {
        set optclass [string range $option 1 end]
    }
    return [list $optdb $optclass]
}


# ------------------------------------------------------------------------------
#  Command Widget::_get_tkwidget_options
# ------------------------------------------------------------------------------
proc Widget::_get_tkwidget_options { tkwidget } {
    variable _tk_widget
    variable _optiondb
    variable _optionclass

    if { ![info exists _tk_widget($tkwidget)] } {
        set widget [$tkwidget ".#BWidget#$tkwidget"]
        set config [$widget configure]
        foreach optlist $config {
            set opt [lindex $optlist 0]
            if { [llength $optlist] == 2 } {
                set refsyn [lindex $optlist 1]
                # search for class
                set idx [lsearch $config [list * $refsyn *]]
                if { $idx == -1 } {
                    if { [string index $refsyn 0] == "-" } {
                        # search for option (tk8.1b1 bug)
                        set idx [lsearch $config [list $refsyn * *]]
                    } else {
                        # last resort
                        set idx [lsearch $config [list -[string tolower $refsyn] * *]]
                    }
                    if { $idx == -1 } {
                        # fed up with "can't read classopt()"
                        return -code error "can't find option of synonym $opt"
                    }
                }
                set syn [lindex [lindex $config $idx] 0]
                set def [lindex [lindex $config $idx] 3]
                lappend _tk_widget($tkwidget) [list $opt $syn $def]
            } else {
                set def [lindex $optlist 3]
                lappend _tk_widget($tkwidget) [list $opt $def]
                set _optiondb($opt)    [lindex $optlist 1]
                set _optionclass($opt) [lindex $optlist 2]
            }
        }
    }
    return $_tk_widget($tkwidget)
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_tkresource
# ------------------------------------------------------------------------------
proc Widget::_test_tkresource { option value arg } {
    set tkwidget [lindex $arg 0]
    set realopt  [lindex $arg 1]
    set path     ".#BWidget#$tkwidget"
    set old      [$path cget $realopt]
    $path configure $realopt $value
    set res      [$path cget $realopt]
    $path configure $realopt $old

    return $res
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_bwresource
# ------------------------------------------------------------------------------
proc Widget::_test_bwresource { option value arg } {
    return -code error "bad option type BwResource in widget"
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_synonym
# ------------------------------------------------------------------------------
proc Widget::_test_synonym { option value arg } {
    return -code error "bad option type Synonym in widget"
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_string
# ------------------------------------------------------------------------------
proc Widget::_test_string { option value arg } {
    return $value
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_flag
# ------------------------------------------------------------------------------
proc Widget::_test_flag { option value arg } {
    set len [string length $value]
    set res ""
    for {set i 0} {$i < $len} {incr i} {
        set c [string index $value $i]
        if { [string first $c $arg] == -1 } {
            return -code error "bad [string range $option 1 end] value \"$value\": characters must be in \"$arg\""
        }
        if { [string first $c $res] == -1 } {
            append res $c
        }
    }
    return $res
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_enum
# ------------------------------------------------------------------------------
proc Widget::_test_enum { option value arg } {
    if { [lsearch $arg $value] == -1 } {
        set last [lindex   $arg end]
        set sub  [lreplace $arg end end]
        if { [llength $sub] } {
            set str "[join $sub ", "] or $last"
        } else {
            set str $last
        }
        return -code error "bad [string range $option 1 end] value \"$value\": must be $str"
    }
    return $value
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_int
# ------------------------------------------------------------------------------
proc Widget::_test_int { option value arg } {
    set binf [lindex $arg 0]
    set bsup [lindex $arg 1]
    if { $binf != "" } {set binf ">$binf"}
    if { $bsup != "" } {set bsup "<$bsup"}
    if { [catch {expr $value}] || $value != int($value) ||
         !($binf == "" || [expr $value$binf]) ||
         !($bsup == "" || [expr $value$bsup]) } {
        return -code error "bad [string range $option 1 end] value \"$value\": must be integer $binf $bsup"
    }
    return $value
}


# ------------------------------------------------------------------------------
#  Command Widget::_test_boolean
# ------------------------------------------------------------------------------
proc Widget::_test_boolean { option value arg } {
    if { $value == 1 ||
         ![string compare $value "true"] ||
         ![string compare $value "yes"] } {
        set value 1
    } elseif { $value == 0 ||
               ![string compare $value "false"] ||
               ![string compare $value "no"] } {
        set value 0
    } else {
        return -code error "bad [string range $option 1 end] value \"$value\": must be boolean"
    }
    return $value
}


# ------------------------------------------------------------------------------
#  Command Widget::focusNext
#  Same as tk_focusNext, but call Widget::focusOK
# ------------------------------------------------------------------------------
proc Widget::focusNext { w } {
    set cur $w
    while 1 {

	# Descend to just before the first child of the current widget.

	set parent $cur
	set children [winfo children $cur]
	set i -1

	# Look for the next sibling that isn't a top-level.

	while 1 {
	    incr i
	    if {$i < [llength $children]} {
		set cur [lindex $children $i]
		if {[winfo toplevel $cur] == $cur} {
		    continue
		} else {
		    break
		}
	    }

	    # No more siblings, so go to the current widget's parent.
	    # If it's a top-level, break out of the loop, otherwise
	    # look for its next sibling.

	    set cur $parent
	    if {[winfo toplevel $cur] == $cur} {
		break
	    }
	    set parent [winfo parent $parent]
	    set children [winfo children $parent]
	    set i [lsearch -exact $children $cur]
	}
	if {($cur == $w) || [focusOK $cur]} {
	    return $cur
	}
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::focusPrev
#  Same as tk_focusPrev, but call Widget::focusOK
# ------------------------------------------------------------------------------
proc Widget::focusPrev { w } {
    set cur $w
    while 1 {

	# Collect information about the current window's position
	# among its siblings.  Also, if the window is a top-level,
	# then reposition to just after the last child of the window.
    
	if {[winfo toplevel $cur] == $cur}  {
	    set parent $cur
	    set children [winfo children $cur]
	    set i [llength $children]
	} else {
	    set parent [winfo parent $cur]
	    set children [winfo children $parent]
	    set i [lsearch -exact $children $cur]
	}

	# Go to the previous sibling, then descend to its last descendant
	# (highest in stacking order.  While doing this, ignore top-levels
	# and their descendants.  When we run out of descendants, go up
	# one level to the parent.

	while {$i > 0} {
	    incr i -1
	    set cur [lindex $children $i]
	    if {[winfo toplevel $cur] == $cur} {
		continue
	    }
	    set parent $cur
	    set children [winfo children $parent]
	    set i [llength $children]
	}
	set cur $parent
	if {($cur == $w) || [focusOK $cur]} {
	    return $cur
	}
    }
}


# ------------------------------------------------------------------------------
#  Command Widget::focusOK
#  Same as tk_focusOK, but handles -editable option and whole tags list.
# ------------------------------------------------------------------------------
proc Widget::focusOK { w } {
    set code [catch {$w cget -takefocus} value]
    if { $code == 1 } {
        return 0
    }
    if {($code == 0) && ($value != "")} {
	if {$value == 0} {
	    return 0
	} elseif {$value == 1} {
	    return [winfo viewable $w]
	} else {
	    set value [uplevel \#0 $value $w]
            if {$value != ""} {
		return $value
	    }
        }
    }
    if {![winfo viewable $w]} {
	return 0
    }
    set code [catch {$w cget -state} value]
    if {($code == 0) && ($value == "disabled")} {
	return 0
    }
    set code [catch {$w cget -editable} value]
    if {($code == 0) && !$value} {
        return 0
    }

    set top [winfo toplevel $w]
    foreach tags [bindtags $w] {
        if { [string compare $tags $top]  &&
             [string compare $tags "all"] &&
             [regexp Key [bind $tags]] } {
            return 1
        }
    }
    return 0
}
