#
# Copyright (c) 1993 Eric Schenk.
# All rights reserved.
#
# Permission is hereby granted, without written agreement and without
# license or royalty fees, to use, copy, modify, and distribute this
# software and its documentation for any purpose, provided that the
# above copyright notice and the following two paragraphs appear in
# all copies of this software.
# 
# IN NO EVENT SHALL ERIC SCHENK BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIC
# SCHENK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ERIC SCHENK SPECIFICALLY DISCLAIMS ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
# ON AN "AS IS" BASIS, AND ERIC SCHENK HAS NO OBLIGATION TO
# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

# This is somewhat gory, but I don't see how to make it simpler.

# Variables used:
#	tkwm_priv(geometry): The final requested geometry
#	tkwm_priv(done):     Boolean flag to wait on
#
#	tkwm_priv(left): The left edge of the window
#	tkwm_priv(right): The right edge of the window
#	tkwm_priv(top): The top edge of the window
#	tkwm_priv(bottom): The bottom edge of the window
#
#	tkwm_priv(orig_left): The original left edge of the window
#	tkwm_priv(orig_right): The original right edge of the window
#	tkwm_priv(orig_top): The original top edge of the window
#	tkwm_priv(orig_bottom): The original bottom edge of the window
#
#	tkwm_priv(none): A dummy variable
#
#	tkwm_priv(min_width):
#	tkwm_priv(min_height):
#	tkwm_priv(max_width):
#	tkwm_priv(max_height):
#	tkwm_priv(max_aspect):
#	tkwm_priv(min_aspect):
#
#	tkwm_priv(height):
#	tkwm_priv(base_height):
#	tkwm_priv(height_inc):
#	tkwm_priv(width):
#	tkwm_priv(base_width):
#	tkwm_priv(width_inc):
#
#	tkwm_priv(opaque): Do opaque resizing (very CPU intensive!)

proc resize {w event} {
    exclusive-dispatch [list real-resize $w $event]
}

proc optional_compute_resize {w x y} {
    if {![tkwm compressMotion]} {
	tkwm_compute_resize $w $x $y
    }
}

proc real-resize {w event} {
    global tkwm_priv

    scan [tkwm pointer] "%d %d" x y
    # this is a fixed width label to avoid interfering with the resize
    # border crossing the label. If the label changes size when the border
    # crosses it it leaves inverted bits behind on any window that overlaps
    # the label.
    label .notifier -text "" -width 12 -height 1 -rel raised -border 2 -cursor crosshair
    place .notifier -x 0 -y -100

    unwindProtect {
	if {![guarded_raise $w $event]} {
	    bind all $event [list tkwm_end_resize $w $event %X %Y]
	    bind all <Any-Motion> [list optional_compute_resize $w %X %Y]

	    set tkwm_priv(opaque) [get_resource_value \
		$w opaqueResize OpaqueResize]

	    if !$tkwm_priv(opaque) {tkwm grabserver}
	    grab -global .notifier
	    install_root_colormap
	    unwindProtect {
		place .notifier -x 0 -y 0
		tkwm_resize_init $w
		tkwm_compute_resize $w $x $y

		tkwait variable tkwm_priv(done)

		catch {
		    # just in case the window is deleted out from under us
		    wm geometry $w $tkwm_priv(geometry)
		    # and we may not be resizing a client plug
		    $w.plug sendconfig
		}
		update idletasks
	    } {
		catch {uninstall_root_colormap}
		catch {grab release .notifier}
		catch {if !$tkwm_priv(opaque) {tkwm ungrabserver}}
	    }
	}
    } {
	catch {destroy .notifier}
    }
}

proc tkwm_end_resize {w event x y} {
    global tkwm_priv

    unwindProtect {
	bind all $event {}
	bind all <Any-Motion> {}
	tkwm_compute_resize $w $x $y
	tkwm outline 0 0 0 0
	destroy .notifier
    } {
	set tkwm_priv(done) 1
    }
}

# This procedure changes the current tool according to the location of the
# mouse with respect to the last known resizing outline.

proc tkwm_change_resize_tool {x y} {
    global tkwm_priv

    if {$x <= $tkwm_priv(orig_left) && $tkwm_priv(xedge) != "left"} {
	set tkwm_priv(xedge) left
	set tkwm_priv(right) $tkwm_priv(orig_right)
    }
    if {$x >= $tkwm_priv(orig_right) && $tkwm_priv(xedge) != "right"} {
	set tkwm_priv(xedge) right
	set tkwm_priv(left) $tkwm_priv(orig_left)
    }
    if {$y <= $tkwm_priv(orig_top) && $tkwm_priv(yedge) != "top"} {
	set tkwm_priv(yedge) top
	set tkwm_priv(bottom) $tkwm_priv(orig_bottom)
    }
    if {$y >= $tkwm_priv(orig_bottom) && $tkwm_priv(yedge) != "bottom"} {
	set tkwm_priv(yedge) bottom
	set tkwm_priv(top) $tkwm_priv(orig_top)
    }
    # FIXME: fix the cursor
}

proc tkwm_resize_init {w} {
    global tkwm_priv

    set wd [winfo width $w]
    set ht [winfo height $w]

    set tkwm_priv(left) [winfo x $w]
    set tkwm_priv(right) [expr $tkwm_priv(left)+$wd-1]
    set tkwm_priv(top) [winfo y $w]
    set tkwm_priv(bottom) [expr $tkwm_priv(top)+$ht-1]
    set tkwm_priv(none) 0

    set tkwm_priv(orig_left) $tkwm_priv(left)
    set tkwm_priv(orig_right) $tkwm_priv(right)
    set tkwm_priv(orig_top) $tkwm_priv(top)
    set tkwm_priv(orig_bottom) $tkwm_priv(bottom)

    # set the tool edges
    set tkwm_priv(xedge) none
    set tkwm_priv(yedge) none

    # get the minimum and maximum widths and the min and max aspect ratios
    handleError {
	set tkwm_priv(min_width) [tkwm info min_width $w]
	set tkwm_priv(min_height) [tkwm info min_height $w]
	set tkwm_priv(max_width) [tkwm info max_width $w]
	set tkwm_priv(max_height) [tkwm info max_height $w]
	set tkwm_priv(min_aspect) [tkwm info min_aspect $w]
	set tkwm_priv(max_aspect) [tkwm info max_aspect $w]
        # figure out how much space the borders are using
	set wdo [expr $wd-[winfo width $w.plug]]
	set hto [expr $ht-[winfo height $w.plug]]
	# adjust sizes
        incr tkwm_priv(min_width) $wdo
        catch {incr tkwm_priv(max_width) $wdo}
        incr tkwm_priv(min_height) $hto
        catch {incr tkwm_priv(max_height) $hto}
    } {
	# the window is not a TKWM client, use the internal TK settings...
	set ms [wm minsize $w]
	set tkwm_priv(min_width) 0
	set tkwm_priv(min_height) 0
	scan $ms "%d %d" tkwm_priv(min_width) tkwm_priv(min_height)
	set ms [wm maxsize $w]
	set tkwm_priv(max_width) Infinity
	set tkwm_priv(max_height) Infinity
	scan $ms "%d %d" tkwm_priv(max_width) tkwm_priv(max_height)
        set ms [wm aspect $w]
	scan $ms "%d %d %d %d" minNumer minDenom maxNumer maxDenom
        handleError {
	    set tkwm_priv(min_aspect) [expr float($minNumer)/float($minDenom)]
	    set tkwm_priv(max_aspect) [expr float($maxNumer)/float($maxDenom)]
	} {
	    set tkwm_priv(min_aspect) None
	    set tkwm_priv(max_aspect) None
	}
    }

    # set the geometry gridding information
    scan [wm geometry $w] "%dx%d" tkwm_priv(width) tkwm_priv(height)
    handleError {
	set tkwm_priv(width_inc) [tkwm info width_inc $w]
	set tkwm_priv(height_inc) [tkwm info height_inc $w]
	set tkwm_priv(width) [expr "($tkwm_priv(width)-[tkwm info base_width $w])/$tkwm_priv(width_inc)"]
	set tkwm_priv(height) [expr "($tkwm_priv(height)-[tkwm info base_height $w])/$tkwm_priv(height_inc)"]
    } {
        set num [scan [wm grid $w] "%d %d %d %d" d1 d2 \
	  tkwm_priv(width_inc) tkwm_priv(height_inc)]
        if {$num < "4"} {
	    set tkwm_priv(width_inc) 1
	    set tkwm_priv(height_inc) 1
        }
    }
    set tkwm_priv(base_width) $tkwm_priv(width)
    set tkwm_priv(base_height) $tkwm_priv(height)
}

proc tkwm_compute_resize {w x y} {
    global tkwm_priv

    # change resizing tools if needed.
    tkwm_change_resize_tool $x $y

    # propose some new borders (snapped to grid)
    if {$tkwm_priv(xedge)!="none"} {
        set ox $tkwm_priv(orig_$tkwm_priv(xedge))
        set wi $tkwm_priv(width_inc)
        if {[expr "$x-$ox"]>="0"} {set sg "1"} {set sg "-1"}
        set tkwm_priv($tkwm_priv(xedge)) [expr "$ox+$sg*(abs($x-$ox)/$wi)*$wi"]
    }
    if {$tkwm_priv(yedge)!="none"} {
        set oy $tkwm_priv(orig_$tkwm_priv(yedge))
        set hi $tkwm_priv(height_inc)
        if {[expr "$y-$oy"]>="0"} {set sg "1"} {set sg "-1"}
        set tkwm_priv($tkwm_priv(yedge)) [expr "$oy+$sg*(abs($y-$oy)/$hi)*$hi"]
    }

    # the new width and height
    set wd [expr $tkwm_priv(right)-$tkwm_priv(left)+1]
    set ht [expr $tkwm_priv(bottom)-$tkwm_priv(top)+1]

    # adjust height and width for aspect ratios
    # I really have no good idea of what this should do!

    # adjust height and width for min and max sizes
    if {$wd < $tkwm_priv(min_width)} {set wd $tkwm_priv(min_width)}
    if {$ht < $tkwm_priv(min_height)} {set ht $tkwm_priv(min_height)}
    if {$wd > $tkwm_priv(max_width)} {set wd $tkwm_priv(max_width)}
    if {$ht > $tkwm_priv(max_height)} {set ht $tkwm_priv(max_height)}

    # compute the new height and width in grid units
    set wc [expr $wd-($tkwm_priv(orig_right)-$tkwm_priv(orig_left)+1)]
    set hc [expr $ht-($tkwm_priv(orig_bottom)-$tkwm_priv(orig_top)+1)]
    set tkwm_priv(width) [expr $tkwm_priv(base_width)+$wc/$tkwm_priv(width_inc)]
    set tkwm_priv(height) [expr $tkwm_priv(base_height)+$hc/$tkwm_priv(height_inc)]

    # fix the borders to meet the new sizes
    if {$tkwm_priv(xedge) == "left"} { set tkwm_priv(left) [expr $tkwm_priv(right)-$wd+1] }
    if {$tkwm_priv(xedge) == "right"} { set tkwm_priv(right) [expr $tkwm_priv(left)+$wd-1] }
    if {$tkwm_priv(yedge) == "top"} { set tkwm_priv(top) [expr $tkwm_priv(bottom)-$ht+1] }
    if {$tkwm_priv(yedge) == "bottom"} { set tkwm_priv(bottom) [expr $tkwm_priv(top)+$ht-1] }

    handleError {
	set wd2 [expr "$tkwm_priv(width)*$tkwm_priv(width_inc)+[tkwm info base_width $w]"]
	set ht2 [expr "$tkwm_priv(height)*$tkwm_priv(height_inc)+[tkwm info base_height $w]"]
	set tkwm_priv(geometry) "${wd2}x${ht2}+$tkwm_priv(left)+$tkwm_priv(top)"
    } {
	set tkwm_priv(geometry) \
	 "$tkwm_priv(width)x$tkwm_priv(height)+$tkwm_priv(left)+$tkwm_priv(top)"
    }
    if $tkwm_priv(opaque) {
	wm geometry $w $tkwm_priv(geometry)
    } else {
	tkwm outline $tkwm_priv(left) $tkwm_priv(top) $wd $ht
    }

    .notifier configure -text "$tkwm_priv(width)x$tkwm_priv(height)"
    update idletasks
}
