#
# Scrolledlistbox
# ----------------------------------------------------------------------
# Implements a scrolled listbox with additional options to manage
# horizontal and vertical scrollbars.  This includes options to control
# which scrollbars are displayed and the method, i.e. statically,
# dynamically, or none at all.  
#
# WISH LIST:
#   This section lists possible future enhancements.  
#
#   1) Option to position scrollbars, i.e. vertical: left/right and
#      horizontal: top/bottom, taking the filler into account.
#
# ----------------------------------------------------------------------
#  AUTHOR: Mark L. Ulferts               EMAIL: mulferts@spd.dsccc.com
#
#  @(#) $Id: scrolledlistbox.itk,v 1.1 1998/07/27 18:49:49 stanton Exp $
# ----------------------------------------------------------------------
#            Copyright (c) 1995 DSC Technologies Corporation
# ======================================================================
# Permission to use, copy, modify, distribute and license this software 
# and its documentation for any purpose, and without fee or written 
# agreement with DSC, is hereby granted, provided that the above copyright 
# notice appears in all copies and that both the copyright notice and 
# warranty disclaimer below appear in supporting documentation, and that 
# the names of DSC Technologies Corporation or DSC Communications 
# Corporation not be used in advertising or publicity pertaining to the 
# software without specific, written prior permission.
# 
# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
# SOFTWARE.
# ======================================================================

#
# Default resources.
#
option add *Scrolledlistbox.borderWidth 2 widgetDefault
option add *Scrolledlistbox.relief sunken widgetDefault
option add *Scrolledlistbox.scrollMargin 3 widgetDefault
option add *Scrolledlistbox.vscrollMode static widgetDefault
option add *Scrolledlistbox.hscrollMode static widgetDefault
option add *Scrolledlistbox.selectmode browse widgetDefault
option add *Scrolledlistbox.width 0 widgetDefault
option add *Scrolledlistbox.height 0 widgetDefault
option add *Scrolledlistbox.visibleItems 20x10 widgetDefault
option add *Scrolledlistbox.labelPos n widgetDefault

#
# Usual options.
#
itk::usual Scrolledlistbox {
    keep -activebackground -activerelief -background -borderwidth -cursor \
	 -elementborderwidth -foreground -highlightcolor -highlightthickness \
	 -jump -labelfont -selectbackground -selectborderwidth \
	 -selectforeground -textbackground -textfont -troughcolor 
}

# ------------------------------------------------------------------
#                          SCROLLEDLISTBOX
# ------------------------------------------------------------------
class iwidgets::Scrolledlistbox {
    inherit iwidgets::Labeledwidget

    constructor {args} {}
    destructor {}

    itk_option define -scrollmargin scrollMargin Margin 3
    itk_option define -sbwidth sbWidth Width 15
    itk_option define -vscrollmode vscrollMode ScrollMode static
    itk_option define -hscrollmode hscrollMode ScrollMode static
    itk_option define -items items Items {}
    itk_option define -dblclickcommand dblClickCommand Command {}
    itk_option define -selectioncommand selectionCommand Command {}
    itk_option define -selectmode selectMode SelectMode browse
    itk_option define -width width Width 0
    itk_option define -height height Height 0
    itk_option define -visibleitems visibleItems VisibleItems 20x10

    public method curselection {} 
    public method activate {index} 
    public method bbox {index} 
    public method clear {} 
    public method see {index} 
    public method index {index} 
    public method delete {first {last {}}} 
    public method get {first {last {}}} 
    public method getcurselection {} 
    public method insert {index string args} 
    public method nearest {y} 
    public method scan {option args} 
    public method selection {option first {last {}}} 
    public method size {} 
    public method selecteditemcount {} 
    public method justify {direction} 
    public method sort {mode} 
    public method xview {args} 
    public method yview {args} 

    protected method _makeSelection {} 
    protected method _dblclick {} 
    protected method _scrollList {wid first last} 
    protected method _updateFiller {}
    protected method _configureFiller {}
    protected method _fixIndex {index} 

    private method _fillerWidth {} 
    private method _vertScrollbarDisplay {mode} 
    private method _horizScrollbarDisplay {mode} 

    protected variable _vmode off              ;# Vertical scroll mode
    protected variable _hmode off              ;# Vertical scroll mode
    protected variable _selection {}           ;# Last selection indcies.
    protected variable _reconfigPlanned ""     ;# non-null, update pending

    private variable _initialized 0            ;# Initialization flag.
}

#
# Provide a lowercased access method for the Scrolledlistbox class.
# 
proc ::iwidgets::scrolledlistbox {pathName args} {
    uplevel ::iwidgets::Scrolledlistbox $pathName $args
}

# ------------------------------------------------------------------
#                        CONSTRUCTOR
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::constructor {args} {
    component hull configure -borderwidth 0
    
    #
    # Create some frames to hold both the top and bottom halfs of the 
    # widget.  The top will contain both the list and vertical scroll 
    # bar.  The bottom houses the horizontal scrollbar and some filler.
    #
    itk_component add listframe {
	frame $itk_interior.listframe
    } {
	keep -background -cursor
    }
    pack $itk_component(listframe) -fill both -expand yes
    
    itk_component add bottomframe {
	frame $itk_component(listframe).botttomframe
    } {
	keep -background -cursor
    }
    pack $itk_component(bottomframe) -fill x -side bottom
    
    itk_component add hmargin {
	frame $itk_component(listframe).hmargin
    } {
	keep -background -cursor
    }
    pack $itk_component(hmargin) -side bottom
    
    itk_component add topframe {
	frame $itk_component(listframe).topframe
    } {
	keep -background -cursor
    }
    pack $itk_component(topframe) -fill both -expand yes
    
    # 
    # Create the listbox.
    #
    itk_component add listbox {
	listbox $itk_component(topframe).listbox \
		-width 1 -height 1 \
	        -xscrollcommand \
		[code $this _scrollList $itk_component(bottomframe).horizsb] \
		-yscrollcommand \
		[code $this _scrollList $itk_component(topframe).vertsb]
    } {
	keep -borderwidth -cursor -exportselection \
		-foreground -highlightcolor \
		-highlightthickness -relief -selectbackground \
		-selectborderwidth -selectforeground -selectmode
	
	rename -font -textfont textFont Font
	rename -background -textbackground textBackground Background
	rename -highlightbackground -background background Background
    }
    
    #
    # Create the vertical margin
    #
    itk_component add vmargin {
	frame $itk_component(topframe).vmargin -width 0
    } {
	keep -background -cursor
    }
    
    # 
    # Create the vertical scroll bar.
    #
    itk_component add vertsb {
	scrollbar $itk_component(topframe).vertsb -orient vertical \
		-command [code $itk_component(listbox) yview]
    } {
	keep -activebackground -activerelief -background -borderwidth \
		-cursor -elementborderwidth \
		-highlightcolor -jump -highlightthickness -relief \
		-repeatdelay -repeatinterval -troughcolor
	rename -highlightbackground -background background Background
    }
    pack $itk_component(vertsb) -side right -fill y
    pack $itk_component(vmargin) -side right
    pack $itk_component(listbox) -fill both -expand yes -side left
    
    #
    # Next the horizontal scrollbar.
    #
    itk_component add horizsb {
	scrollbar $itk_component(bottomframe).horizsb -orient horizontal \
		-command [code $itk_component(listbox) xview]
    } {
	keep -activebackground -activerelief -background -borderwidth \
		-cursor -elementborderwidth \
		-highlightcolor -jump -highlightthickness -relief \
		-repeatdelay -repeatinterval -troughcolor
	rename -highlightbackground -background background Background
    }
    pack $itk_component(horizsb) -side left -fill x -expand yes
    
    #
    # Create the filler frame and compute its width.
    #
    itk_component add filler {
	frame $itk_component(bottomframe).filler -width [_fillerWidth]
    } {
	keep -background -cursor
    }
    pack $itk_component(filler) -side right 
    
    # 
    # Create a set of bindings for monitoring the selection.
    #
    bind SLBSelect$this <Control-Key-backslash> [code $this _makeSelection]
    bind SLBSelect$this <Control-Key-slash> [code $this _makeSelection]
    bind SLBSelect$this <Key-Escape> [code $this _makeSelection]
    bind SLBSelect$this <Shift-Key-Select> [code $this _makeSelection]
    bind SLBSelect$this <Control-Shift-Key-space> [code $this _makeSelection]
    bind SLBSelect$this <Key-Select> [code $this _makeSelection]
    bind SLBSelect$this <Key-space> [code $this _makeSelection]
    bind SLBSelect$this <Control-Shift-Key-End> [code $this _makeSelection]
    bind SLBSelect$this <Control-Key-End> [code $this _makeSelection]
    bind SLBSelect$this <Control-Shift-Key-Home> [code $this _makeSelection]
    bind SLBSelect$this <Control-Key-Home> [code $this _makeSelection]
    bind SLBSelect$this <Shift-Key-Down> [code $this _makeSelection]
    bind SLBSelect$this <Shift-Key-Up> [code $this _makeSelection]
    bind SLBSelect$this <Control-Button-1> [code $this _makeSelection]
    bind SLBSelect$this <Shift-Button-1> [code $this _makeSelection]
    bind SLBSelect$this <ButtonRelease-1> [code $this _makeSelection]
    bind SLBSelect$this <Double-1> [code $this _dblclick]

    bindtags $itk_component(listbox) \
	    [linsert [bindtags $itk_component(listbox)] end SLBSelect$this]

    #
    # Explicitly handle configs that may have been ignored earlier.
    #
    eval itk_initialize $args
    
    bind $itk_component(hmargin) <Configure> [code $this _configureFiller]
    bind $itk_component(vertsb) <Configure> [code $this _configureFiller]
    bind $itk_component(vmargin) <Configure> [code $this _configureFiller]
    bind $itk_component(horizsb) <Configure> [code $this _configureFiller]

    set _initialized 1
    configure -scrollmargin $itk_option(-scrollmargin)
}

# ------------------------------------------------------------------
#                           DESTURCTOR
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::destructor {} {
    if {$_reconfigPlanned != ""} {after cancel $_reconfigPlanned}
}

# ------------------------------------------------------------------
#                             OPTIONS
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# OPTION: -scrollmargin
#
# Set the distance between the scrollbars and the list box.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::scrollmargin {
    if {$_initialized} {
	set pixels [winfo pixels $itk_component(hmargin) \
		$itk_option(-scrollmargin)]
	
	if {$_hmode == "on"} {
	    $itk_component(hmargin) config -width 1 -height $pixels
	}
	
	if {$_vmode == "on"} {
	    $itk_component(vmargin) config -width $pixels -height 1
	}
	
	$itk_component(filler) config -width [_fillerWidth] -height 1
    }
}

# ------------------------------------------------------------------
# OPTION: -sbwidth
#
# Set the width of the scrollbars.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::sbwidth {
    if {$_initialized} {
	$itk_component(vertsb) configure -width $itk_option(-sbwidth)
	$itk_component(horizsb) configure -width $itk_option(-sbwidth)
    }
}

# ------------------------------------------------------------------
# OPTION: -vscrollmode
#
# Enable/disable display and mode of veritcal scrollbars.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::vscrollmode {
    switch $itk_option(-vscrollmode) {
	static {
	    _vertScrollbarDisplay on
	}
	
	dynamic -
	none {
	    _vertScrollbarDisplay off
	}
	
	default {
	    error "bad vscrollmode option\
		    \"$itk_option(-vscrollmode)\": should be\
		    static, dynamic, or none"
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -hscrollmode
#
# Enable/disable display and mode of horizontal scrollbars.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::hscrollmode {
    switch $itk_option(-hscrollmode) {
	static {
	    _horizScrollbarDisplay on
	}
	
	dynamic -
	none {
	    _horizScrollbarDisplay off
	}
	
	default {
	    error "bad hscrollmode option\
		    \"$itk_option(-hscrollmode)\": should be\
		    static, dynamic, or none"
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -items
#
# Set/get the current list of items in the listbox.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::items {
    set index [$itk_component(listbox) nearest 0]
    
    $itk_component(listbox) delete 0 end
    
    foreach ent $itk_option(-items) {
	$itk_component(listbox) insert end $ent
    }
    
    if {$index < [$itk_component(listbox) size]} {
	$itk_component(listbox) yview $index
    }
}

# ------------------------------------------------------------------
# OPTION: -dblclickcommand
#
# Specify a command to be executed upon double click of a listbox 
# item.  Also, create a couple of bindings used for specific
# selection modes
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::dblclickcommand {}

# ------------------------------------------------------------------
# OPTION: -selectioncommand
#
# Specifies a command to be executed upon selection of a listbox 
# item.  The command will be called upon each selection regardless 
# of selection mode..
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::selectioncommand {}

# ------------------------------------------------------------------
# OPTION: -width
#
# Specifies the width of the scrolled list box as an entire unit.
# The value may be specified in any of the forms acceptable to 
# Tk_GetPixels.  Any additional space needed to display the other
# components such as labels, margins, and scrollbars force the listbox
# to be compressed.  A value of zero along with the same value for 
# the height causes the value given for the visibleitems option 
# to be applied which administers geometry constraints in a different
# manner.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::width {
    if {$itk_option(-width) != 0} {
	pack propagate $itk_component(shell) no
	
	$itk_component(listbox) configure -width 1
	$itk_component(shell) configure \
		-width [winfo pixels \
		$itk_component(shell) $itk_option(-width)] 
    } else {
	if {$_initialized} {
	    configure -visibleitems $itk_option(-visibleitems)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -height
#
# Specifies the height of the scrolled list box as an entire unit.
# The value may be specified in any of the forms acceptable to 
# Tk_GetPixels.  Any additional space needed to display the other
# components such as labels, margins, and scrollbars force the listbox
# to be compressed.  A value of zero along with the same value for 
# the width causes the value given for the visibleitems option 
# to be applied which administers geometry constraints in a different
# manner.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::height {
    if {$itk_option(-height) != 0} {
	pack propagate $itk_component(shell) no
	
	$itk_component(listbox) configure -height 1
	$itk_component(shell) configure \
		-height [winfo pixels \
		$itk_component(shell) $itk_option(-height)] 
    } else {
	if {$_initialized} {
	    configure -visibleitems $itk_option(-visibleitems)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -visibleitems
#
# Specified the widthxheight in characters and lines for the listbox.
# This option is only administered if the width and height options
# are both set to zero, otherwise they take precedence.  With the
# visibleitems option engaged, geometry constraints are maintained
# only on the listbox.  The size of the other components such as 
# labels, margins, and scroll bars, are additive and independent, 
# effecting the overall size of the scrolled list box.  In contrast,
# should the width and height options have non zero values, they
# are applied to the scrolled list box as a whole.  The listbox 
# is compressed or expanded to maintain the geometry constraints.
# ------------------------------------------------------------------
configbody iwidgets::Scrolledlistbox::visibleitems {
    if {[regexp {^[0-9]+x[0-9]+$} $itk_option(-visibleitems)]} {
	if {($itk_option(-width) == 0) && \
		($itk_option(-height) == 0)} {
	    set chars [lindex [split $itk_option(-visibleitems) x] 0]
	    set lines [lindex [split $itk_option(-visibleitems) x] 1]
	    
	    pack propagate $itk_component(shell) yes
	    
	    $itk_component(listbox) configure \
		    -width $chars -height $lines
	}
	
    } else {
	error "bad visibleitems option\
		\"$itk_option(-visibleitems)\": should be\
		widthxheight"
    }
}

# ------------------------------------------------------------------
#                            METHODS
# ------------------------------------------------------------------

# ------------------------------------------------------------------
# METHOD: curselection 
#
# Returns a list containing the indices of all the elements in the 
# listbox that are currently selected.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::curselection {} {
    return [$itk_component(listbox) curselection]
}

# ------------------------------------------------------------------
# METHOD: activate index
#
# Sets the active element to the one indicated by index.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::activate {index} {
    return [$itk_component(listbox) activate [_fixIndex $index]]
}

# ------------------------------------------------------------------
# METHOD: bbox index
#
# Returns four element list describing the bounding box for the list
# item at index
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::bbox {index} {
    return [$itk_component(listbox) bbox [_fixIndex $index]]
}

# ------------------------------------------------------------------
# METHOD clear 
#
# Clear the listbox area of all items.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::clear {} {
    delete 0 end
}

# ------------------------------------------------------------------
# METHOD: see index
#
# Adjusts the view such that the element given by index is visible.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::see {index} {
    $itk_component(listbox) see [_fixIndex $index]
}

# ------------------------------------------------------------------
# METHOD: index index
#
# Returns the decimal string giving the integer index corresponding 
# to index.  The index value may be a integer number, active,
# anchor, end, @x,y, or a pattern.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::index {index} {
    if {[regexp {(^[0-9]+$)|(active)|(anchor)|(end)|(^@[0-9]+,[0-9]+$)} $index]} {
	return [$itk_component(listbox) index $index]
	
    } else {
	set indexValue [lsearch -glob $itk_option(-items) $index]
	
	if {$indexValue == -1} {
	    error "bad Scrolledlistbox index \"$index\": must be\
		    active, anchor, end, @x,y, number, or a pattern"
	}
	
	return $indexValue
    }
}

# ------------------------------------------------------------------
# METHOD: _fixIndex index
#
# Similar to the regular "index" method, but it only converts
# the index to a numerical value if it is a string pattern.  If
# the index is in the proper form to be used with the listbox,
# it is left alone.  This fixes problems associated with converting
# an index such as "end" to a numerical value.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_fixIndex {index} {
    if {[regexp {(^[0-9]+$)|(active)|(anchor)|(end)|(^@[0-9]+,[0-9]+$)} $index]} {
	return $index
	
    } else {
	set indexValue [lsearch -glob $itk_option(-items) $index]
	
	if {$indexValue == -1} {
	    error "bad Scrolledlistbox index \"$index\": must be\
		    active, anchor, end, @x,y, number, or a pattern"
	}
	
	return $indexValue
    }
}

# ------------------------------------------------------------------
# METHOD: delete first ?last?
#
# Delete one or more elements from list box based on the first and 
# last index values.  Indexes may be a number, active, anchor, end,
# @x,y, or a pattern.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::delete {first {last {}}} {
    set first [_fixIndex $first]
    
    if {$last != {}} {
	set last [_fixIndex $last]
    } else {
	set last $first
    }
    
    if {[llength $itk_option(-items)] > 0} {
	if {$first <= $last} {
	    configure -items [lreplace $itk_option(-items) $first $last]
	} else {
	    error "first index must not be greater than second"
	}
    }
}

# ------------------------------------------------------------------
# METHOD: get first ?last?
#
# Returns the elements of the listbox indicated by the indexes. 
# Indexes may be a number, active, anchor, end, @x,y, ora pattern.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::get {first {last {}}} {
    set first [_fixIndex $first]

    if {$last != ""} {
	set last [_fixIndex $last]
    }
    
    if {$last == {}} {
	return [$itk_component(listbox) get $first]
    } else {
	return [$itk_component(listbox) get $first $last]
    }
}

# ------------------------------------------------------------------
# METHOD: getcurselection 
#
# Returns the contents of the listbox element indicated by the current 
# selection indexes.  Short cut version of get and curselection 
# command combination.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::getcurselection {} {
    set rlist {}

    if {[selecteditemcount] > 0} {
	set cursels [$itk_component(listbox) curselection]
    
	switch $itk_option(-selectmode) {
	    single -
	    browse {
		set rlist [$itk_component(listbox) get $cursels]
	    }

	    multiple -
	    extended {
		foreach sel $cursels {
		    lappend rlist [$itk_component(listbox) get $sel]
		}
	    }
	}
    }
    
    return $rlist
}

# ------------------------------------------------------------------
# METHOD: insert index string ?string ...?
#
# Insert zero or more elements in the list just before the element 
# given by index.  Modify the $itk_option(-items) value as well.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::insert {index string args} {
    set index [_fixIndex $index]
    set args [linsert $args 0 $string]

    if {[catch {eval $itk_component(listbox) insert $index $args} \
	    result] == 0} {
	
	configure -items [eval linsert {$itk_option(-items)} \
		$index $args]
	
    } else {
	return $result
    }
}

# ------------------------------------------------------------------
# METHOD: nearest y
#
# Given a y-coordinate within the listbox, this command returns the 
# index of the visible listbox element nearest to that y-coordinate.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::nearest {y} {
    $itk_component(listbox) nearest $y
}

# ------------------------------------------------------------------
# METHOD: scan option args 
#
# Implements scanning on listboxes.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::scan {option args} {
    eval $itk_component(listbox) scan $option $args
}

# ------------------------------------------------------------------
# METHOD: selection option first ?last?
#
# Adjusts the selection within the listbox.  The index value may be 
# a integer number, active, anchor, end, @x,y, or a pattern.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::selection {option first {last {}}} {
    set first [_fixIndex $first]

    if {$last != "" && $last != "end"} {
	set last [_fixIndex $last]
    }
    
    switch $option {
	anchor {
	    $itk_component(listbox) selection anchor $first
	}
	
	clear {
	    if {$last == {}} {
		$itk_component(listbox) selection clear $first 
	    } else {
		$itk_component(listbox) selection clear $first $last
	    }
	}
	
	includes {
	    $itk_component(listbox) selection includes $first
	}
	
	set {
	    if {$last == {}} {
		$itk_component(listbox) selection set $first 
	    } else {
		$itk_component(listbox) selection set $first $last
	    }
	}
    }
}

# ------------------------------------------------------------------
# METHOD: size 
#
# Returns a decimal string indicating the total number of elements 
# in the listbox.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::size {} {
    return [$itk_component(listbox) size]
}

# ------------------------------------------------------------------
# METHOD: selecteditemcount 
#
# Returns a decimal string indicating the total number of selected 
# elements in the listbox.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::selecteditemcount {} {
    return [llength [$itk_component(listbox) curselection]]
}

# ------------------------------------------------------------------
# METHOD: justify direction
#
# Justifies the list scrolled region in one of four directions: top,
# bottom, left, or right.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::justify {direction} {
    switch $direction {
	left { 
	    $itk_component(listbox) xview moveto 0
	}
	right {
	    $itk_component(listbox) xview moveto 1
	}
	top {
	    $itk_component(listbox) yview moveto 0
	}
	bottom {
	    $itk_component(listbox) yview moveto 1
	}
	default {
	    error "bad justify argument \"$direction\": should\
		    be left, right, top, or bottom"
	}
    }
}

# ------------------------------------------------------------------
# METHOD: sort mode
#
# Sort the current list in either "ascending/increasing" or 
# "descending/decreasing" order.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::sort {mode} {
    switch $mode {
	ascending -
	increasing {
	    configure -items [lsort -increasing $itk_option(-items)]
	}
	descending -
	decreasing {
	    configure -items [lsort -decreasing $itk_option(-items)]
	}
	default {
	    error "bad sort argument \"$mode\": should be\
		    ascending, descending, increasing, or decreasing"
	}
    }
}

# ------------------------------------------------------------------
# METHOD: xview args
#
# Change or query the vertical position of the text in the list box.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::xview {args} {
    return [eval $itk_component(listbox) xview $args]
}

# ------------------------------------------------------------------
# METHOD: yview args
#
# Change or query the horizontal position of the text in the list box.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::yview {args} {
    return [eval $itk_component(listbox) yview $args]
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _fillerWidth 
#
# Compute the width of the filler frame based on the vertical 
# scrollbar width plus the margin.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_fillerWidth {} {
    if {$_vmode == "on"} {
	return [expr [winfo reqwidth $itk_component(vertsb)] + \
		[winfo reqwidth $itk_component(vmargin)]]
    } else {
	return 1
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _configureFiller 
#
# Set up to do an update of the filler if one is not all ready 
# planned.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_configureFiller {} {
    if {$_reconfigPlanned == ""} {
	set _reconfigPlanned [after idle [code $this _updateFiller]]
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _updateFiller 
#
# Update the width of the filler following a configure event on 
# a scrollbar or margin.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_updateFiller {} {
    $itk_component(filler) config -width [_fillerWidth] -height 1
    set _reconfigPlanned ""
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _makeSelection 
#
# Evaluate the selection command.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_makeSelection {} {
    uplevel #0 $itk_option(-selectioncommand)
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _dblclick 
#
# Evaluate the double click command option if not empty.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_dblclick {} {
    uplevel #0 $itk_option(-dblclickcommand)
}	

# ------------------------------------------------------------------
# PRIVATE METHOD: _vertScrollbarDisplay mode
#
# Displays the vertical scrollbar based on the input mode.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_vertScrollbarDisplay {mode} {
    switch $mode  {
	on {
	    set _vmode on
	    
	    $itk_component(vmargin) config -height 1 -width \
		    [winfo pixels $itk_component(vmargin) \
		    $itk_option(-scrollmargin)]
	    
	    pack forget $itk_component(listbox)
	    pack forget $itk_component(vmargin)
	    pack $itk_component(vertsb) -side right -fill y
	    pack $itk_component(vmargin) -side right
	    pack $itk_component(listbox) -fill both -expand yes -side left 
	}
	
	off {
	    set _vmode off
	    
	    pack forget $itk_component(vertsb)
	    
	    $itk_component(vmargin) config -width 1 -height 1
	    $itk_component(filler) config -width 1 -height 1
	}
	
	default {
	    error "invalid argument \"$mode\": should be on or off"
	}
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _horizScrollbarDisplay mode
#
# Displays the horizontal scrollbar based on the input mode.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_horizScrollbarDisplay {mode} {
    switch $mode  {
	on {
	    set _hmode on
	    
	    $itk_component(hmargin) config -width 1 -height \
		    [winfo pixels $itk_component(hmargin) \
		    $itk_option(-scrollmargin)]
	    
	    pack $itk_component(horizsb) -side left -fill x -expand yes
	}
	
	off {
	    set _hmode off
	    
	    pack forget $itk_component(horizsb)
	    
	    $itk_component(hmargin) config -width 1 -height 1
	    $itk_component(filler) config -width 1 -height 1
	}
	
	default {
	    error "invalid argument \"$mode\": should be on or off"
	}
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _scrollList wid first last
#
# Performs scrolling and display of scrollbars based on the first and
# last listbox views as well as the current -vscrollmode and -hscrollmode
# settings.
# ------------------------------------------------------------------
body iwidgets::Scrolledlistbox::_scrollList {wid first last} {
    $wid set $first $last
    
    if {$wid == $itk_component(vertsb)} {
	if {$itk_option(-vscrollmode) == "dynamic"} {
	    if {($first == 0) && ($last == 1)} {
		if {$_vmode != "off"} {
		    _vertScrollbarDisplay off
		}
		
	    } else {
		if {$_vmode != "on"} {
		    _vertScrollbarDisplay on
		}
	    }
	}
	
    } elseif {$wid == $itk_component(horizsb)} {
	if {$itk_option(-hscrollmode) == "dynamic"} {
	    if {($first == 0) && ($last == 1)} {
		if {$_hmode != "off"} {
		    _horizScrollbarDisplay off
		}
		
	    } else {
		if {$_hmode != "on"} {
		    _horizScrollbarDisplay on
		}
	    }
	}
    }
}
