# utils.tcl --
#
#	Various support routines.
#

# WindowBringToFront --
#
#	Description
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc WindowBringToFront windowPath {

	wm deiconify $windowPath
	raise $windowPath
	focus [focus -lastfor $windowPath]
}

# WindowPosition --
#
#	Description
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc WindowPosition {win x y} {

	# Save the current focus
	set oldFocus [focus]

    # Withdraw the window, then update all the geometry information
    # so we know how big it wants to be, then center the window in the
    # display and de-iconify it.

    wm withdraw $win
    update idletasks
    set x2 [expr ([winfo screenwidth $win] - [winfo reqwidth $win])/$x \
	    - [winfo vrootx $win]]
    set y2 [expr ([winfo screenheight $win] - [winfo reqheight $win])/$y \
	    - [winfo vrooty $win]]
    wm geometry $win +$x2+$y2

	# Seems required on Windows
	update idletasks

    wm deiconify $win

	# Restore focus
	catch {focus $oldFocus}
}

namespace eval NSUtils {

variable UtilsPriv
set UtilsPriv(grabDepth) 0

# NSUtils::GrabSave --
#
#	Sets a grab on the given window, saving the current focus and
#	grab for a later call to GrabRelease().
#
# Arguments:
#	arg1				about arg1
#
# Results:
#	What happened.

proc GrabSave win {

	variable UtilsPriv

	set depth [incr UtilsPriv(grabDepth)]

	set UtilsPriv(grab,$depth,win) $win
	set UtilsPriv(grab,$depth,oldFocus) [focus]
	set UtilsPriv(grab,$depth,oldGrab) [grab current $win]
	if {$UtilsPriv(grab,$depth,oldGrab) != ""} {
		set UtilsPriv(grab,$depth,grabStatus) \
			[grab status $UtilsPriv(grab,$depth,oldGrab)]
	}
	grab $win
}

# NSUtils::GrabRelease --
#
#	Releases the grab and restores the focus saved in GrabSave().
#
# Arguments:
#	arg1				about arg1
#
# Results:
#	What happened.

proc GrabRelease win {

	variable UtilsPriv

	set depth $UtilsPriv(grabDepth)
	incr UtilsPriv(grabDepth) -1

	if {$UtilsPriv(grab,$depth,win) != $win} {
		error "grab release on wrong window"
	}

	catch {focus $UtilsPriv(grab,$depth,oldFocus)}
	grab release $win
	if {$UtilsPriv(grab,$depth,oldGrab) != ""} {
		if {$UtilsPriv(grab,$depth,grabStatus) == "global"} {
		    grab -global $UtilsPriv(grab,$depth,oldGrab)
		} else {
		    grab $UtilsPriv(grab,$depth,oldGrab)
		}
	}

	foreach name [array names "grab,$depth,*"] {
		unset UtilsPriv($name)
	}
}

# NSUtils::StringBox --
#
#	Put up a window to get a string from the user.
#
# Arguments:
#	arg1				about arg1
#
# Results:
#	What happened.

proc StringBox {title initial prompt buttons parent} {

    variable UtilsPriv

    set win .stringbox

    toplevel $win -class Dialog
    wm title $win $title
    wm resizable $win no no
    wm transient $win $parent

	set frame $win.frame
	frame $frame \
		-borderwidth 0
    label $frame.label -text "$prompt:"
    entry $frame.text -width 30

	# Initial text
    $frame.text insert 0 $initial

	set frame $win.frameButton
	frame $frame \
		-borderwidth 0

	set n 1
	foreach text $buttons {
	    button $frame.button$n \
			-text $text -command "set NSUtils::UtilsPriv(result) $n" \
			-width 9
		pack $frame.button$n \
			-side right -padx 5 -pady 0
		bind $frame.button$n <ButtonPress-1> {+focus %W}
		incr n
	}

	# Set the left-most button to the default button
	set defNum [expr $n - 1]
    $frame.button$defNum configure -default active

    # "pathname flash" doesn't work!
    bind $frame.button1 <KeyPress-Return> "tkButtonInvoke $frame.button1"

    # Return key selects default button while typing
    bind $win.frame.text <KeyPress-Return> "
        tkButtonInvoke $frame.button$defNum
    "

    # Escape key selects "cancel" button while typing
    bind $win.frame.text <KeyPress-Escape> "
        tkButtonInvoke $frame.button1
    "

	pack $win.frame \
		-side top -padx 5 -pady 5
	pack $win.frame.label \
		-side left -padx 5 -pady 5
	pack $win.frame.text \
		-side left -padx 5 -pady 5
	pack $win.frameButton \
		-side top -padx 5 -pady 0 -anchor e
	pack [frame $win.framePad1 -borderwidth 0 -height 10] \
		-side top

	# <Destroy> handler sets UtilsPriv(result)
	bind $win <Destroy> "set NSUtils::UtilsPriv(result) 1"

	# Position window
	WindowPosition $win 2 3

	# Set up a grab and claim focus too
	GrabSave $win
    focus $win.frame.text

    # Select the text
    $win.frame.text selection range 0 end

    # Wait for a button press
    set UtilsPriv(result) ""
    tkwait variable NSUtils::UtilsPriv(result)

	# Release grab and reset focus
	GrabRelease $win
	
    set result ""

    if {[winfo exists $win]} {
        switch $UtilsPriv(result) \
			$defNum {set result [$win.frame.text get]}
    }

    # Maybe the window is already destroyed
    catch {
		bind $win <Destroy> {}
		destroy $win
	}

    return $result
}

# NSUtils::ZapLabel --
#
#	Clears the given label (if it exists). Usually called as an
#	"after" command.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ZapLabel label {

	set text [$label cget -text]
	regsub -all \" $text "\\\"" text

	after 1200 "
		if \[winfo exists $label] {
			if !\[string compare \[$label cget -text] \"$text\"] {
				$label configure -text {}
			}
		}
	"
}

# NSUtils::GetBoolean --
#
#	Almost the same as Tcl_GetBoolean(). Given a string, return
#	0 if the string is 0/false/no/off, return 1 if the string is
#	1/true/yes/on. Case insensitive. Generate an error otherwise.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GetBoolean string {

	if {[catch {
		set result [expr {$string ? 1 : 0}]
	}]} {
		error "expected boolean value but got \"$string\""
	}
	return $result
}

# NSUtils::HasFocus --
#
#	Return 1 if the given window has the focus.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc HasFocus window {

	if {![string compare [focus -displayof $window] $window]} {
		return 1
	}
	return 0
}

# namespace eval NSUtils
}
