#
# Panedwindow
# ----------------------------------------------------------------------
# Implements a multiple paned window widget capable of orienting the panes
# either vertically or horizontally.  Each pane is itself a frame acting
# as a child site for other widgets.  The border separating each pane 
# contains a sash which allows user positioning of the panes relative to
# one another.  
#
# WISH LIST:
#   This section lists possible future enhancements.  
#
#   1) Add an option to support squeezable panes.  Say you had more than
#      two panes and you start to move the bottom one up, currently
#      the sash movement is stopped when the minimum of the above pane
#      is reached.  It be nice if movement could continue if any of
#      the upper panes still has room, until all the upper panes
#      are at minimum.  This is just an example which must be greatly
#      generalized to be useful.
#
# ----------------------------------------------------------------------
#  AUTHOR: Mark L. Ulferts               EMAIL: mulferts@spd.dsccc.com
#
#  @(#) $Id: panedwindow.itk,v 1.1 1998/07/27 18:49:44 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 *Panedwindow.orient horizontal widgetDefault
option add *Panedwindow.sashBorderWidth 2 widgetDefault
option add *Panedwindow.sashCursor crosshair widgetDefault
option add *Panedwindow.sashIndent -10 widgetDefault
option add *Panedwindow.sashHeight 10 widgetDefault
option add *Panedwindow.sashWidth 10 widgetDefault
option add *Panedwindow.thickness 3 widgetDefault
option add *Panedwindow.width 10 widgetDefault
option add *Panedwindow.height 10 widgetDefault

#
# Usual options.
#
itk::usual Panedwindow {
    keep -background -cursor -sashcursor
}

# ------------------------------------------------------------------
#                            PANEDWINDOW
# ------------------------------------------------------------------
class iwidgets::Panedwindow {
    inherit itk::Widget

    constructor {args} {}

    itk_option define -orient orient Orient horizontal 
    itk_option define -sashborderwidth sashBorderWidth SashBorderWidth 2
    itk_option define -sashcursor sashCursor SashCursor crosshair
    itk_option define -sashwidth sashWidth SashWidth 10
    itk_option define -sashheight sashHeight SashHeight 10
    itk_option define -thickness thickness Thickness 3
    itk_option define -sashindent sashIndent SashIndent -10

    public method index {index} 
    public method childsite {args} 
    public method fraction {percentage1 percentage2 args} 
    public method add {tag args} 
    public method insert {index tag args} 
    public method delete {index} 
    public method hide {index}    
    public method show {index} 
    public method paneconfigure {index args} 
    public method reset {} 

    protected method _pwConfigureEventHandler {width height} 
    protected method _startGrip {where num} 
    protected method _endGrip {where num} 
    protected method _configGrip {where num} 
    protected method _handleGrip {where num} 
    protected method _moveSash {where num} 

    private method _setFracArray {} 
    private method _setActivePanes {} 
    private method _calcFraction {where num} 
    private method _makeSashes {} 
    private method _placeSash {i} 
    private method _placePanes {{start 0} {end end}} 
    
    private variable _initialized 0    ;# Denotes initialized state.
    private variable _panes {}         ;# List of panes.
    private variable _activePanes {}   ;# List of active panes.
    private variable _sashes {}        ;# List of sashes.
    private variable _separators {}    ;# List of separators.
    private variable _frac             ;# Array of fraction percentages.
    private variable _lowerlimit       ;# Margin distance above/left of sash.
    private variable _upperlimit       ;# Margin distance below/right of sash.
    private variable _dimension        ;# Width/Height at start of drag.
    private variable _sashloc          ;# Array of dist of sash from above/left.
    private variable _pixels           ;# Array of dist of sash from above/left.
    private variable _minheight        ;# Array of min heights for panes.
    private variable _minsashmoved     ;# Lowest sash moved during dragging.
    private variable _maxsashmoved     ;# Highest sash moved during dragging.
    private variable _dragging 0       ;# Boolean for dragging enabled.
    private variable _movecount 0      ;# Kludge counter to get sashes to
                                       ;# display without calling update 
                                       ;# idletasks too often.
    private variable _width 0          ;# hull's width.
    private variable _height 0         ;# hull's height.
    private variable _unique -1         ;# Unique number for pane names.
}

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

# ------------------------------------------------------------------
#                        CONSTRUCTOR
# ------------------------------------------------------------------
body iwidgets::Panedwindow::constructor {args} {
    itk_option add hull.width hull.height

    pack propagate $itk_component(hull) no
    
    #
    # Add binding for the configure event.
    #
    bind pw-config-$this <Configure> [code $this _pwConfigureEventHandler %w %h]
    bindtags $itk_component(hull) \
	    [linsert [bindtags $itk_component(hull)] 0 pw-config-$this]
    
    eval itk_initialize $args
}

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

# ------------------------------------------------------------------
# OPTION: -orient
#
# Specifies the orientation of the sashes.  Once the paned window
# has been mapped, set the sash bindings and place the panes.
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::orient {
    if {$_initialized} {
	switch $itk_option(-orient) {
	    vertical {
		for {set i 1} {$i < [llength $_panes]} {incr i} {
		    bind $itk_component(sash$i) <Button-1> \
			    [code $this _startGrip %x $i]
		    bind $itk_component(sash$i) <B1-Motion> \
			    [code $this _handleGrip %x $i]
		    bind $itk_component(sash$i) <B1-ButtonRelease-1> \
			    [code $this _endGrip %x $i]
		    bind $itk_component(sash$i) <Configure> \
			    [code $this _configGrip %x $i]
		}
		
		_setFracArray
		_makeSashes
		_placePanes
	    }
	    
	    horizontal {
		for {set i 1} {$i < [llength $_panes]} {incr i} {
		    bind $itk_component(sash$i) <Button-1> \
			    [code $this _startGrip %y $i]
		    bind $itk_component(sash$i) <B1-Motion> \
			    [code $this _handleGrip %y $i]
		    bind $itk_component(sash$i) <B1-ButtonRelease-1> \
			    [code $this _endGrip %y $i]
		    bind $itk_component(sash$i) <Configure> \
			    [code $this _configGrip %y $i]
		}
		
		_setFracArray
		_makeSashes
		_placePanes
	    }
	    
	    default {
		error "bad orientation option \"$itk_option(-orient)\":\
			should be horizontal or vertical"
	    }
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -sashborderwidth
#
# Specifies a non-negative value indicating the width of the 3-D
# border to draw around the outside of the sash.
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::sashborderwidth {
    set pixels [winfo pixels $itk_component(hull) \
	    $itk_option(-sashborderwidth)]
    set itk_option(-sashborderwidth) $pixels
    
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    $itk_component(sash$i) configure \
		    -borderwidth $itk_option(-sashborderwidth)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -sashcursor
#
# Specifies the type of cursor to be used when over the sash.
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::sashcursor {
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    $itk_component(sash$i) configure \
		    -cursor $itk_option(-sashcursor)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -sashwidth
#
# Specifies the width of the sash.
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::sashwidth {
    set pixels [winfo pixels $itk_component(hull) \
	    $itk_option(-sashwidth)]
    set itk_option(-sashwidth) $pixels
    
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    $itk_component(sash$i) configure \
		    -width $itk_option(-sashwidth)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -sashheight
#
# Specifies the height of the sash,
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::sashheight {
    set pixels [winfo pixels $itk_component(hull) \
	    $itk_option(-sashheight)]
    set itk_option(-sashheight) $pixels
    
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    $itk_component(sash$i) configure \
		    -height $itk_option(-sashheight)
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -thickness
#
# Specifies the thickness of the separators.  It sets the width and
# height of the separator to the thickness value and the borderwidth
# to half the thickness.
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::thickness {
    set pixels [winfo pixels $itk_component(hull) \
	    $itk_option(-thickness)]
    set itk_option(-thickness) $pixels
    
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    $itk_component(separator$i) configure \
		    -height $itk_option(-thickness)
	    $itk_component(separator$i) configure \
		    -width $itk_option(-thickness)
	    $itk_component(separator$i) configure \
		    -borderwidth [expr $itk_option(-thickness) / 2]
	}
	
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    _placeSash $i
	}
    }
}

# ------------------------------------------------------------------
# OPTION: -sashindent
#
# Specifies the placement of the sash along the panes.  A positive
# value causes the sash to be offset from the near (left/top) side
# of the pane, and a negative value causes the sash to be offset from
# the far (right/bottom) side.  If the offset is greater than the 
# width, then the sash is placed flush against the side.  
# ------------------------------------------------------------------
configbody iwidgets::Panedwindow::sashindent {
    set pixels [winfo pixels $itk_component(hull) \
	    $itk_option(-sashindent)]
    set itk_option(-sashindent) $pixels
    
    if {$_initialized} {
	for {set i 1} {$i < [llength $_panes]} {incr i} {
	    _placeSash $i
	}
    }
}

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

# ------------------------------------------------------------------
# METHOD: index index
#
# Searches the panes in the paned window for the one with the 
# requested tag, numerical index, or keyword "end".  Returns the pane's 
# numerical index if found, otherwise error.
# ------------------------------------------------------------------    
body iwidgets::Panedwindow::index {index} {
    if {[llength $_panes] > 0} {
	if {[regexp {(^[0-9]+$)} $index]} {
	    if {$index < [llength $_panes]} {
		return $index
	    } else {
		error "Panedwindow index \"$index\" is out of range"
	    }
	    
	} elseif {$index == "end"} {
	    return [expr [llength $_panes] - 1]
	    
	} else {
	    if {[set idx [lsearch $_panes $index]] != -1} {
		return $idx
	    }
	    
	    error "bad Panedwindow index \"$index\": must be number, end,\
		    or pattern"
	}
	
    } else {
	error "Panedwindow \"$itk_component(hull)\" has no panes"
    }
}

# ------------------------------------------------------------------
# METHOD: childsite ?index?
#
# Given an index return the specifc childsite path name.  Invoked 
# without an index return a list of all the child site panes.  The 
# list is ordered from the near side (left/top).
# ------------------------------------------------------------------
body iwidgets::Panedwindow::childsite {args} {
    if {! $_initialized} {
	set _initialized 1
	reset
    }

    if {[llength $args] == 0} {
	set children {}
	
	foreach pane $_panes {
	    lappend children [$itk_component($pane) childSite]
	}
	
	return $children
	
    } else {
	set index [index [lindex $args 0]]
	return [$itk_component([lindex $_panes $index]) childSite]
    }
}

# ------------------------------------------------------------------
# METHOD: fraction percentage percentage ?percentage ...?
#
# Sets the visible percentage of the panes.  Specifies a list of
# percentages which are applied to the currently visible panes from 
# the near side (left/top).  The number of percentages must be equal 
# to the current number of visible (mapped) panes and add up to 100.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::fraction {percentage1 percentage2 args} {
    set args [linsert $args 0 $percentage1 $percentage2]
    
    if {[llength $args] == [llength $_activePanes]} {
	set sum 0
	
	for {set i 0} {$i < [llength $args]} {incr i} {
	    set sum [expr $sum + [lindex $args $i]]
	}
	
	if {$sum == 100} {
	    set perc 0.0
	    
	    for {set i 0} {$i < [llength $_activePanes]} {incr i} {
		set _frac($i) $perc
		set perc [expr $perc + [expr [lindex $args $i] / 100.0]]
	    }
	    
	    set _frac($i) 1.0
	    
	    if {[winfo ismapped $itk_component(hull)]} {
		_placePanes
	    }
	    
	} else {
	    error "bad fraction arguments \"$args\": they should add\
		    up to 100"
	}
	
    } else {
	error "wrong # args: should be \"$itk_component(hull)\
		fraction percentage percentage ?percentage ...?\",\
		where the number of percentages is\
		[llength $_activePanes] and equal 100"
    }
}

# ------------------------------------------------------------------
# METHOD: add tag ?option value option value ...?
#
# Add a new pane to the paned window to the far (right/bottom) side.
# The method takes additional options which are passed on to the 
# pane constructor.  These include -margin, and -minimum.  The path 
# of the pane is returned.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::add {tag args} {
    #
    # Create panes.
    #
    itk_component add $tag {
	eval iwidgets::Pane $itk_interior.pane[incr _unique] $args
    } {
	keep -background -cursor
    }
    
    lappend _panes $tag
    lappend _activePanes $tag
    
    reset
    
    return $itk_component($tag)
}

# ------------------------------------------------------------------
# METHOD: insert index tag ?option value option value ...?
#
# Insert the specified pane in the paned window just before the one 
# given by index.  Any additional options which are passed on to the 
# pane constructor.  These include -margin, -minimum.  The path of 
# the pane is returned.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::insert {index tag args} {
    #
    # Create panes.
    #
    itk_component add $tag {
	eval iwidgets::Pane $itk_interior.pane[incr _unique] $args
    } {
	keep -background -cursor
    }
    
    set index [index $index]
    set _panes [linsert $_panes $index $tag]
    lappend _activePanes $tag
    
    reset
    
    return $itk_component($tag)
}

# ------------------------------------------------------------------
# METHOD: delete index
#
# Delete the specified pane.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::delete {index} {
    set index [index $index]
    set tag [lindex $_panes $index]
    
    destroy $itk_component($tag)
    
    set _panes [lreplace $_panes $index $index]
    
    reset
}

# ------------------------------------------------------------------
# METHOD: hide index
#
# Remove the specified pane from the paned window. 
# ------------------------------------------------------------------
body iwidgets::Panedwindow::hide {index} {
    set index [index $index]
    set tag [lindex $_panes $index]
    
    if {[set idx [lsearch -exact $_activePanes $tag]] != -1} {
	set _activePanes [lreplace $_activePanes $idx $idx]
    }
    
    reset
}   

# ------------------------------------------------------------------
# METHOD: show index
#
# Display the specified pane in the paned window.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::show {index} {
    set index [index $index]
    set tag [lindex $_panes $index]
    
    if {[lsearch -exact $_activePanes $tag] == -1} {
	lappend _activePanes $tag
    }
    
    reset
}   

# ------------------------------------------------------------------
# METHOD: paneconfigure index ?option? ?value option value ...?
#
# Configure a specified pane.  This method allows configuration of
# panes from the Panedwindow level.  The options may have any of the 
# values accepted by the add method.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::paneconfigure {index args} {
    set index [index $index]
    set tag [lindex $_panes $index]
    
    return [uplevel $itk_component($tag) configure $args]
}

# ------------------------------------------------------------------
# METHOD: reset
#
# Redisplay the panes based on the default percentages of the panes.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::reset {} {
    if {$_initialized && [llength $_panes]} {
	_setActivePanes
	_setFracArray
	
	_makeSashes
	_placePanes
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _pwConfigureEventHandler
#
# Performs operations necessary following a configure event.  This
# includes placing the panes.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_pwConfigureEventHandler {width height} {
    set _width $width
    set _height $height
    if {$_initialized} {
	_placePanes
    } else {
	set _initialized 1
	reset
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _startGrip where num
#
# Starts the sash drag and drop operation.  At the start of the drag
# operation all the information is known as for the upper and lower
# limits for sash movement.  The calculation is made at this time and
# stored in protected variables for later access during the drag
# handling routines.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_startGrip {where num} {
    if {$itk_option(-orient) == "horizontal"} {
      set _dimension $_height
    } else {
      set _dimension $_width
    }
    
    set _minsashmoved $num
    set _maxsashmoved $num
    set totMinHeight 0
    set cnt [llength $_activePanes]
    set _sashloc(0) 0
    set _pixels($cnt) [expr int($_dimension)]
    for {set i 0} {$i < $cnt} {incr i} {
      set _pixels($i) [expr int($_frac($i) * $_dimension)]
      set margaft [$itk_component([lindex $_activePanes $i]) cget -margin]
      set minaft [$itk_component([lindex $_activePanes $i]) cget -minimum]
      set _minheight($i) [expr $minaft + (2 * $margaft)]
      incr totMinHeight $_minheight($i)
    }
    set _dragging [expr $_dimension > $totMinHeight]

    grab  $itk_component(sash$num)
    raise $itk_component(separator$num)
    raise $itk_component(sash$num)
    
    $itk_component(sash$num) configure -relief sunken
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _endGrip where num
#
# Ends the sash drag and drop operation.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_endGrip {where num} {
    $itk_component(sash$num) configure -relief raised
    grab release $itk_component(sash$num)
    if {$_dragging} {
      _calcFraction [expr $_sashloc($num) + $where] $num
      _placePanes [expr $_minsashmoved - 1] $_maxsashmoved
      set _dragging 0
    }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _configGrip where num
#
# Configure  action for sash.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_configGrip {where num} {
   set _sashloc($num) $where
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _handleGrip where num
#
# Motion action for sash.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_handleGrip {where num} {
 if {$_dragging} {
  _moveSash [expr $where + $_sashloc($num)] $num
  incr _movecount
  if {$_movecount>4} {
    set _movecount 0
    update idletasks
  }
 }
}

# ------------------------------------------------------------------
# PROTECTED METHOD: _moveSash where num
#
# Move the sash to the absolute pixel location
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_moveSash {where num} {
  set _minsashmoved [expr ($_minsashmoved<$num)?$_minsashmoved:$num]
  set _maxsashmoved [expr ($_maxsashmoved>$num)?$_maxsashmoved:$num]
  set oldfrac $_frac($num)
  _calcFraction $where $num
  if {$_frac($num)!=$oldfrac} { _placeSash $num }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _setFracArray
#
# Calculates the percentages for the fraction array which lists the
# percentages for each pane.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_setFracArray {} {
    if {[llength [array names _frac]] != [expr [llength $_activePanes] + 1]} {
	set perc 0.0
	set percIncr [expr 1.0 / [llength $_activePanes]]
    
	for {set i 0} {$i < [llength $_activePanes]} {incr i} {
	    set _frac($i) $perc
	    set perc [expr $perc + $percIncr]
	}
    
	set _frac($i) 1.0
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _setActivePanes
#
# Resets the active pane list.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_setActivePanes {} {
    set _prevActivePanes $_activePanes

    set _activePanes {}
    
    foreach pane $_panes {
	if {[lsearch -exact $_prevActivePanes $pane] != -1} {
	    lappend _activePanes $pane
	}
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _calcFraction where num
#
# Determines the fraction for the sash.  Make sure the fraction does
# not go past the minimum for the pane on each side of the separator.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_calcFraction {where num} {
    
    set _lowerlimit \
	    [expr $_pixels([expr $num - 1]) + $_minheight([expr $num - 1])]
    set _upperlimit \
	    [expr $_pixels([expr $num + 1]) - $_minheight($num)]
    
    set dir [expr $where - $_pixels($num)]

    if {$where < $_lowerlimit && $dir <= 0} {
     if {$num == 1} {
      set _pixels($num) $_lowerlimit
     } {
      _moveSash [expr $where - $_minheight([expr $num - 1])] [expr $num -1]
      set _pixels($num) \
	     [expr $_pixels([expr $num - 1]) + $_minheight([expr $num - 1])]
     }
    } elseif {$where > $_upperlimit && $dir >= 0} {
     if {[expr $num + 1] == [llength $_activePanes]} {
      set _pixels($num) $_upperlimit
     } {
      _moveSash [expr $where + $_minheight($num)] [expr $num +1]
      set _pixels($num) \
	     [expr $_pixels([expr $num + 1]) - $_minheight($num)]
     }
    } else {
      set _pixels($num) $where
    }
    set _frac($num) [expr $_pixels($num).0 / $_dimension]
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _makeSashes
#
# Removes any previous sashes and separators and creates new one.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_makeSashes {} {
    #
    # Remove any existing sashes and separators.
    #
    foreach sash $_sashes {
	destroy $itk_component($sash)
    }
    
    foreach separator $_separators {
	destroy $itk_component($separator)
    }
    
    set _sashes {}
    set _separators {}
    
    #
    # Create one less separator and sash than the number of panes.  
    #
    for {set id 1} {$id < [llength $_activePanes]} {incr id} {
	itk_component add sash$id {
	    frame $itk_interior.sash$id -relief raised \
		    -borderwidth $itk_option(-sashborderwidth) \
		    -cursor $itk_option(-sashcursor) \
		    -width $itk_option(-sashwidth) \
		    -height $itk_option(-sashheight)
	} {
	    keep -background
	}
	
	lappend _sashes sash$id
	
	switch $itk_option(-orient) {
	    vertical {
		bind $itk_component(sash$id) <Button-1> \
			[code $this _startGrip %x $id]
		bind $itk_component(sash$id) <B1-Motion> \
			[code $this _handleGrip %x $id]
		bind $itk_component(sash$id) <B1-ButtonRelease-1> \
			[code $this _endGrip %x $id]
		bind $itk_component(sash$id) <Configure> \
			    [code $this _configGrip %x $id]
	    }
	    
	    horizontal {
		bind $itk_component(sash$id) <Button-1> \
			[code $this _startGrip %y $id]
		bind $itk_component(sash$id) <B1-Motion> \
			[code $this _handleGrip %y $id]
		bind $itk_component(sash$id) <B1-ButtonRelease-1> \
			[code $this _endGrip %y $id]
		bind $itk_component(sash$id) <Configure> \
			    [code $this _configGrip %y $id]
	    }
	}
	
	itk_component add separator$id {
	    frame $itk_interior.separator$id -relief sunken \
		    -height $itk_option(-thickness) \
		    -width $itk_option(-thickness) \
		    -borderwidth [expr $itk_option(-thickness) / 2]
	} {
	    keep -background -cursor
	}
	
	lappend _separators separator$id
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _placeSash i
#
# Places the position of the sash and separator.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_placeSash {i} {
    
    if {$itk_option(-orient) == "horizontal"} {
	place $itk_component(separator$i) -in $itk_component(hull) \
		-x 0 -relwidth 1 -rely $_frac($i) -anchor w \
		-height $itk_option(-thickness)
	
	if {$itk_option(-sashindent) < 0} {
	    set sashPos [expr $_width + $itk_option(-sashindent)]
	    set sashAnchor e
	} else {
	    set sashPos $itk_option(-sashindent)
	    set sashAnchor w
	}
	
	place $itk_component(sash$i) -in $itk_component(hull) \
		-x $sashPos -rely $_frac($i) -anchor $sashAnchor
	
    } else {
	place $itk_component(separator$i) -in $itk_component(hull) \
		-y 0 -relheight 1 -relx $_frac($i) -anchor n \
		-width $itk_option(-thickness)
	
	if {$itk_option(-sashindent) < 0} {
	    set sashPos [expr $_height + $itk_option(-sashindent)]
	    set sashAnchor s
	} else {
	    set sashPos $itk_option(-sashindent)
	    set sashAnchor n
	}
	
	place $itk_component(sash$i) -in $itk_component(hull) \
		-y $sashPos -relx $_frac($i) -anchor $sashAnchor
    }
}

# ------------------------------------------------------------------
# PRIVATE METHOD: _placePanes
#
# Resets the panes of the window following movement of the sash.
# ------------------------------------------------------------------
body iwidgets::Panedwindow::_placePanes {{start 0} {end end}} {
     if {$end=="end"} { set end [expr [llength $_activePanes] - 1] }
     set _updatePanes [lrange $_activePanes $start $end]
     if {$_updatePanes == $_activePanes} {
       set _forgetPanes $_panes
     } {
       set _forgetPanes $_updatePanes
     }
    foreach pane $_forgetPanes {
	place forget $itk_component($pane)
    }
   
    
    if {$itk_option(-orient) == "horizontal"} {
	set i $start
	foreach pane $_updatePanes {
	    place $itk_component($pane) -in $itk_component(hull) \
		    -x 0 -rely $_frac($i) -relwidth 1 \
		    -relheight [expr $_frac([expr $i + 1]) - $_frac($i)]
	    incr i
	}
	
    } else {
	set i $start
	foreach pane $_updatePanes {
	    place $itk_component($pane) -in $itk_component(hull) \
		    -y 0 -relx $_frac($i) -relheight 1 \
		    -relwidth [expr $_frac([expr $i + 1]) - $_frac($i)]
	    incr i
	}
	
    }

	for {set i [expr $start+1]} {$i <= $end} {incr i} {
	    _placeSash $i
	    raise $itk_component(separator$i)
	    raise $itk_component(sash$i)
	}
}
