# $Id: scroll.tcl,v 2.14 2004/10/02 12:40:54 jfontain Exp $


# generic scroll widget with automatic scrollbars for scrolling widgets with the common tk scroll interface

class scroll {

    proc scroll {this scrollableClass parentPath args} composite {[new frame $parentPath] $args} {
        # scrollable class widget must accept the -[xy]scrollcommand options and [xy]view commands, such as canvas, text, table, ...
        set path $widget::($this,path)
        composite::manage $this [new $scrollableClass $path] scrolled\
            [new scrollbar $path -orient horizontal -highlightthickness 0] horizontal\
            [new scrollbar $path -highlightthickness 0] vertical [new frame $path] filler
        widget::configure $composite::($this,scrolled)\
            -xscrollcommand "scroll::update $this 1 0" -yscrollcommand "scroll::update $this 0 1"
        widget::configure $composite::($this,horizontal) -command "$composite::($this,scrolled,path) xview"
        widget::configure $composite::($this,vertical) -command "$composite::($this,scrolled,path) yview"
        grid propagate $widget::($this,path) 0                  ;# turn off propagation so that setting width and height is possible
        grid $composite::($this,scrolled,path) -sticky nsew -ipadx 0
        grid rowconfigure $path 0 -weight 1
        grid columnconfigure $path 0 -weight 1
        set ($this,0,1,path) $composite::($this,vertical,path)
        set ($this,1,0,path) $composite::($this,horizontal,path)
        set ($this,0,1,map) 1
        set ($this,1,0,map) 1
        composite::complete $this
    }

    proc ~scroll {this} {}

    proc options {this} {
        return [list\
            [list -automatic 1 1]\
            [list -scrollbarborderwidth $widget::option(scrollbar,borderwidth) $widget::option(scrollbar,borderwidth)]\
            [list\
                -scrollbarelementborderwidth\
                $widget::option(scrollbar,elementborderwidth) $widget::option(scrollbar,elementborderwidth)\
            ]\
            [list -scrollbarwidth $widget::option(scrollbar,width) $widget::option(scrollbar,width)]\
            [list -height $widget::option(canvas,height)]\
            [list -horizontal $($this,1,0,map) $($this,1,0,map)]\
            [list -vertical $($this,0,1,map) $($this,0,1,map)]\
            [list -viewthreshold 0 0]\
            [list -width $widget::option(canvas,width)]\
            [list -xscrollcommand {} {}]\
            [list -yscrollcommand {} {}]\
        ]
    }

    proc set-automatic {this value} {
        if {$composite::($this,complete)} {
            error {option -automatic cannot be set dynamically}
        }
    }

    proc set-horizontal {this value} {
        if {$composite::($this,complete)} {
            error {option -horizontal cannot be set dynamically}
        }
        set ($this,1,0,map) $value
    }

    proc set-vertical {this value} {
        if {$composite::($this,complete)} {
            error {option -vertical cannot be set dynamically}
        }
        set ($this,0,1,map) $value
    }

    proc set-viewthreshold {this value} {}

    foreach option {borderwidth elementborderwidth width} {
        proc set-scrollbar$option {this value} "
            \$composite::(\$this,vertical,path) configure -$option \$value
            \$composite::(\$this,horizontal,path) configure -$option \$value
        "
    }

    proc set-height {this value} {
        $widget::($this,path) configure -height $value
    }

    proc set-width {this value} {
        $widget::($this,path) configure -width $value
    }

    proc set-xscrollcommand {this value} {}                                                                 ;# extra scroll commands
    proc set-yscrollcommand {this value} {}

    proc update {this row column first last} {
        if {($last - $first) < $composite::($this,-viewthreshold)} return    ;# eliminate stray events with span in the low percents
        set path $($this,$row,$column,path)
        foreach {previousFirst previousLast} [$path get] {}
        if {($first == $previousFirst) && ($last == $previousLast)} return                                              ;# no change
        $path set $first $last                                                  ;# always set so that get always return valid values
        set visible [llength [grid info $path]]
        if {!$composite::($this,-automatic) || (($last - $first) < 1)} {
            if {!$visible && $($this,$row,$column,map)} {
                grid $path -row $row -column $column -sticky nsew
                grid $composite::($this,filler,path) -sticky nsew -column 1 -row 1
                set ($this,$row,$column,updating) {}                                                ;# prevent infinite oscillations
                ::update idletasks
                unset ($this,$row,$column,updating)
            }
        } elseif {![info exists ($this,$row,$column,updating)]} {
            grid remove $path
            set visible [llength [grid info $($this,$column,$row,path)]]                                  ;# look at other scrollbar
            if {!$visible} {
                grid remove $composite::($this,filler,path)
            }
        }
        if {$row} {                                                                                               ;# X scroll update
            if {[string length $composite::($this,-xscrollcommand)] > 0} {
                uplevel #0 $composite::($this,-xscrollcommand) $first $last
            }
        } else {                                                                                                  ;# Y scroll update
            if {[string length $composite::($this,-yscrollcommand)] > 0} {
                uplevel #0 $composite::($this,-yscrollcommand) $first $last
            }
        }
    }

}
