# focus.tcl --
#
# This file defines several procedures for managing the input
# focus.
#
# @(#) focus.tcl 1.6 94/12/19 17:06:46
#
# Copyright (c) 1994 Sun Microsystems, Inc.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#

# tk_focusNext --
# This procedure is invoked to move the input focus to the next window
# after a given one.  "Next" is defined in terms of the window
# stacking order, with all the windows underneath a given top-level
# (no matter how deeply nested in the hierarchy) considered except
# for frames and toplevels.
#
# Arguments:
# w -		Name of a window:  the procedure will set the focus
#		to the next window after this one in the traversal
#		order.

proc tk_focusNext w {
    global tkPriv
    set tkPriv(focusDir) next
    set cur $w
    if $tkPriv(focusContinue) {
	# The checks agains tkPriv(focusOrigin) here and again below
	# prevent infinite looping in the case where every window under
	# a top-level forwards the focus by calling tk_focusContinue.

	if {$tkPriv(focusOrigin) == $w} {
	    return
	}
    } else {
	set tkPriv(focusOrigin) $w
    }
    if {([winfo toplevel $w] == $w) || ([winfo class $cur] == "Frame")} {
	set parent $w
	set children [winfo children $cur]
	set i 0
    } else {
	set parent [winfo parent $cur]
	set children [winfo children [winfo parent $cur]]
	set i [expr [lsearch -exact $children $cur] + 1]
    }
    while 1 {
	if {$i >= [llength $children]} {
	    if {[winfo toplevel $parent] == $parent} {
		if {$parent == $tkPriv(focusOrigin)} {
		    focus $parent
		    return
		}
		set i 0
	    } else {
		set cur $parent
		set parent [winfo parent $parent]
		set children [winfo children $parent]
		set i [expr [lsearch -exact $children $cur] + 1]
	    }
	    continue
	}
	set cur [lindex $children $i]
	if {$cur == $tkPriv(focusOrigin)}  {
	    focus $cur
	    return
	}
	if {([winfo toplevel $cur] == $cur)
		|| ([winfo class $cur] == "Menubutton")} {
	    incr i
	    continue
	} elseif {[winfo class $cur] == "Frame"} {
	    set parent $cur
	    set children [winfo children $cur]
	    set i 0
	    continue
	}
	focus $cur
	return
    }
}

# tk_focusPrev --
# This procedure is invoked to move the input focus to the previous
# window before a given one.  "Previous" is defined in terms of the
# window stacking order, with all the windows underneath a given
# top-level (no matter how deeply nested in the hierarchy) considered.
#
# Arguments:
# w -		Name of a window:  the procedure will set the focus
#		to the previous window before this one in the traversal
#		order.

proc tk_focusPrev w {
    global tkPriv
    set tkPriv(focusDir) prev
    set cur $w
    if $tkPriv(focusContinue) {
	# The checks agains tkPriv(focusOrigin) here and again below
	# prevent infinite looping in the case where every window under
	# a top-level forwards the focus by calling tk_focusContinue.

	if {$tkPriv(focusOrigin) == $w} {
	    return
	}
    } else {
	set tkPriv(focusOrigin) $w
    }
    if {([winfo toplevel $w] == $w) || ([winfo class $cur] == "Frame")} {
	set parent $w
	set children [winfo children $cur]
	set i [expr [llength $children] - 1]
    } else {
	set parent [winfo parent $cur]
	set children [winfo children [winfo parent $cur]]
	set i [expr [lsearch -exact $children $cur] - 1]
    }
    while 1 {
	if {$i < 0} {
	    if {[winfo toplevel $parent] == $parent} {
		if {$parent == $tkPriv(focusOrigin)} {
		    focus $parent
		    return
		}
		set i [expr [llength $children] - 1]
	    } else {
		set cur $parent
		set parent [winfo parent $parent]
		set children [winfo children $parent]
		set i [expr [lsearch -exact $children $cur] - 1]
	    }
	    continue
	}
	set cur [lindex $children $i]
	if {$cur == $tkPriv(focusOrigin)}  {
	    focus $cur
	    return
	}
	if {([winfo toplevel $cur] == $cur)
		|| ([winfo class $cur] == "Menubutton")} {
	    incr i -1
	    continue
	} elseif {[winfo class $cur] == "Frame"} {
	    set parent $cur
	    set children [winfo children $cur]
	    set i [expr [llength $children] - 1]
	    continue
	}
	focus $cur
	return
    }
}

# tk_focusContinue
#
# If a window doesn't really want to deal with the input focus, it
# invokes this procedure when it gets the focus.  The procedure just
# passes the focus on to the next window, where "next" can be either
# forwards or backwards depending on whether tk_focusNext or tk_focusPrev
# was used to put the focus where it is.
#
# Arguments:
# w -		Name of a window that got the focus but doesn't want it.

proc tk_focusContinue w {
    global tkPriv
    set oldContinue $tkPriv(focusContinue)
    set tkPriv(focusContinue) 1
    if {$tkPriv(focusDir) == "next"}  {
	tk_focusNext $w
    } else {
	tk_focusPrev $w
    }
    set tkPriv(focusContinue) $oldContinue
}

# tk_focusFollowsMouse
#
# If this procedure is invoked, Tk will enter "focus-follows-mouse"
# mode, where the focus is always on whatever window contains the
# mouse.  If this procedure isn't invoked, then the user typically
# has to click on a window to give it the focus.
#
# Arguments:
# None.

proc tk_focusFollowsMouse {} {
    set old [bind all <Enter>]
    set script {
	if {("%d" == "NotifyAncestor") || ("%d" == "NotifyNonlinear")
		|| ("%d" == "NotifyInferior")} {
	    focus %W
	}
    }
    if {$old != ""} {
	bind all <Enter> "$old; $script"
    } else {
	bind all <Enter> $script
    }
}
