1#
2# Panedwindow
3# ----------------------------------------------------------------------
4# Implements a multiple paned window widget capable of orienting the panes
5# either vertically or horizontally.  Each pane is itself a frame acting
6# as a child site for other widgets.  The border separating each pane
7# contains a sash which allows user positioning of the panes relative to
8# one another.
9#
10# ----------------------------------------------------------------------
11#  AUTHOR: Mark L. Ulferts              EMAIL: mulferts@austin.dsccc.com
12#
13#  @(#) $Id: panedwindow.itk,v 1.7 2001/09/06 15:12:46 smithc Exp $
14# ----------------------------------------------------------------------
15#            Copyright (c) 1995 DSC Technologies Corporation
16# ======================================================================
17# Permission to use, copy, modify, distribute and license this software
18# and its documentation for any purpose, and without fee or written
19# agreement with DSC, is hereby granted, provided that the above copyright
20# notice appears in all copies and that both the copyright notice and
21# warranty disclaimer below appear in supporting documentation, and that
22# the names of DSC Technologies Corporation or DSC Communications
23# Corporation not be used in advertising or publicity pertaining to the
24# software without specific, written prior permission.
25#
26# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
28# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
29# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
30# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL
31# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
32# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
33# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
34# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
35# SOFTWARE.
36# ======================================================================
37
38#
39# Usual options.
40#
41itk::usual Panedwindow {
42    keep -background -cursor -sashcursor
43}
44
45# ------------------------------------------------------------------
46#                            PANEDWINDOW
47# ------------------------------------------------------------------
48itcl::class iwidgets::Panedwindow {
49    inherit itk::Widget
50
51    constructor {args} {}
52
53    itk_option define -orient orient Orient horizontal
54    itk_option define -sashborderwidth sashBorderWidth SashBorderWidth 2
55    itk_option define -sashcursor sashCursor SashCursor crosshair
56    itk_option define -sashwidth sashWidth SashWidth 10
57    itk_option define -sashheight sashHeight SashHeight 10
58    itk_option define -thickness thickness Thickness 3
59    itk_option define -sashindent sashIndent SashIndent -10
60    itk_option define -showhandle showHandle ShowHandle 1
61
62    public method index {index}
63    public method childsite {args}
64    public method fraction {args}
65    public method add {tag args}
66    public method insert {index tag args}
67    public method delete {index}
68    public method hide {index}
69    public method show {index}
70    public method paneconfigure {index args}
71    public method reset {}
72
73    protected method _pwConfigureEventHandler {width height}
74    protected method _startGrip {where num}
75    protected method _endGrip {where num}
76    protected method _configGrip {where num}
77    protected method _handleGrip {where num}
78    protected method _moveSash {where num}
79
80    private method _setFracArray {}
81    private method _setActivePanes {}
82    private method _calcFraction {where num}
83    private method _makeSashes {}
84    private method _placeSash {i}
85    private method _placePanes {{start 0} {end end}}
86
87    private variable _initialized 0    ;# Denotes initialized state.
88    private variable _panes {}         ;# List of panes.
89    private variable _activePanes {}   ;# List of active panes.
90    private variable _sashes {}        ;# List of sashes.
91    private variable _separators {}    ;# List of separators.
92    private variable _frac             ;# Array of fraction percentages.
93    private variable _lowerlimit       ;# Margin distance above/left of sash.
94    private variable _upperlimit       ;# Margin distance below/right of sash.
95    private variable _dimension        ;# Width/Height at start of drag.
96    private variable _sashloc          ;# Array of dist of sash from above/left.
97    private variable _pixels           ;# Array of dist of sash from above/left.
98    private variable _minheight        ;# Array of min heights for panes.
99    private variable _minsashmoved     ;# Lowest sash moved during dragging.
100    private variable _maxsashmoved     ;# Highest sash moved during dragging.
101    private variable _dragging 0       ;# Boolean for dragging enabled.
102    private variable _movecount 0      ;# Kludge counter to get sashes to
103                                       ;# display without calling update
104                                       ;# idletasks too often.
105    private variable _width 0          ;# hull's width.
106    private variable _height 0         ;# hull's height.
107    private variable _unique -1         ;# Unique number for pane names.
108
109    private variable _relief          ;# relief for -showhandle
110}
111
112#
113# Provide a lowercased access method for the Panedwindow class.
114#
115proc ::iwidgets::panedwindow {pathName args} {
116    uplevel ::iwidgets::Panedwindow $pathName $args
117}
118
119#
120# Use option database to override default resources of base classes.
121#
122option add *Panedwindow.width 10 widgetDefault
123option add *Panedwindow.height 10 widgetDefault
124
125# ------------------------------------------------------------------
126#                        CONSTRUCTOR
127# ------------------------------------------------------------------
128itcl::body iwidgets::Panedwindow::constructor {args} {
129    itk_option add hull.width hull.height
130
131    pack propagate $itk_component(hull) no
132
133    #
134    # Add binding for the configure event.
135    #
136    bind pw-config-$this <Configure> [itcl::code $this _pwConfigureEventHandler %w %h]
137    bindtags $itk_component(hull) \
138	    [linsert [bindtags $itk_component(hull)] 0 pw-config-$this]
139
140    array set _relief {0 sunken 1 raised}
141
142    eval itk_initialize $args
143}
144
145# ------------------------------------------------------------------
146#                             OPTIONS
147# ------------------------------------------------------------------
148
149# ------------------------------------------------------------------
150# OPTION: -orient
151#
152# Specifies the orientation of the sashes.  Once the paned window
153# has been mapped, set the sash bindings and place the panes.
154# ------------------------------------------------------------------
155itcl::configbody iwidgets::Panedwindow::orient {
156    if {$_initialized} {
157	switch $itk_option(-orient) {
158	    vertical {
159		for {set i 1} {$i < [llength $_activePanes]} {incr i} {
160		    bind $itk_component(sash$i) <Button-1> \
161			    [itcl::code $this _startGrip %x $i]
162		    bind $itk_component(sash$i) <B1-Motion> \
163			    [itcl::code $this _handleGrip %x $i]
164		    bind $itk_component(sash$i) <B1-ButtonRelease-1> \
165			    [itcl::code $this _endGrip %x $i]
166		    bind $itk_component(sash$i) <Configure> \
167			    [itcl::code $this _configGrip %x $i]
168		}
169
170		_setFracArray
171		_makeSashes
172		_placePanes
173	    }
174
175	    horizontal {
176		for {set i 1} {$i < [llength $_activePanes]} {incr i} {
177		    bind $itk_component(sash$i) <Button-1> \
178			    [itcl::code $this _startGrip %y $i]
179		    bind $itk_component(sash$i) <B1-Motion> \
180			    [itcl::code $this _handleGrip %y $i]
181		    bind $itk_component(sash$i) <B1-ButtonRelease-1> \
182			    [itcl::code $this _endGrip %y $i]
183		    bind $itk_component(sash$i) <Configure> \
184			    [itcl::code $this _configGrip %y $i]
185		}
186
187		_setFracArray
188		_makeSashes
189		_placePanes
190	    }
191
192	    default {
193		error "bad orientation option \"$itk_option(-orient)\":\
194			should be horizontal or vertical"
195	    }
196	}
197    }
198}
199
200# ------------------------------------------------------------------
201# OPTION: -sashborderwidth
202#
203# Specifies a non-negative value indicating the width of the 3-D
204# border to draw around the outside of the sash.
205# ------------------------------------------------------------------
206itcl::configbody iwidgets::Panedwindow::sashborderwidth {
207    set pixels [winfo pixels $itk_component(hull) \
208	    $itk_option(-sashborderwidth)]
209    set itk_option(-sashborderwidth) $pixels
210
211    if {$_initialized} {
212	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
213	    $itk_component(sash$i) configure \
214		    -borderwidth $itk_option(-sashborderwidth)
215	}
216    }
217}
218
219# ------------------------------------------------------------------
220# OPTION: -sashcursor
221#
222# Specifies the type of cursor to be used when over the sash.
223# ------------------------------------------------------------------
224itcl::configbody iwidgets::Panedwindow::sashcursor {
225    if {$_initialized} {
226	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
227	    $itk_component(sash$i) configure -cursor $itk_option(-sashcursor)
228	}
229    }
230}
231
232# ------------------------------------------------------------------
233# OPTION: -sashwidth
234#
235# Specifies the width of the sash.
236# ------------------------------------------------------------------
237itcl::configbody iwidgets::Panedwindow::sashwidth {
238    set pixels [winfo pixels $itk_component(hull) \
239	    $itk_option(-sashwidth)]
240    set itk_option(-sashwidth) $pixels
241
242    if {$_initialized} {
243	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
244	    $itk_component(sash$i) configure \
245		    -width $itk_option(-sashwidth)
246	}
247    }
248}
249
250# ------------------------------------------------------------------
251# OPTION: -sashheight
252#
253# Specifies the height of the sash,
254# ------------------------------------------------------------------
255itcl::configbody iwidgets::Panedwindow::sashheight {
256    set pixels [winfo pixels $itk_component(hull) \
257	    $itk_option(-sashheight)]
258    set itk_option(-sashheight) $pixels
259
260    if {$_initialized} {
261	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
262	    $itk_component(sash$i) configure \
263		    -height $itk_option(-sashheight)
264	}
265    }
266}
267
268# ------------------------------------------------------------------
269# OPTION: -showhandle
270#
271# Specifies whether or not to show the sash handle. If not, then the
272# whole separator becomes the handle.  Valid values are 0 or 1.
273# ------------------------------------------------------------------
274itcl::configbody iwidgets::Panedwindow::showhandle {
275  switch $itk_option(-showhandle) {
276    0 - 1 {
277      # Update the sashes.
278      _makeSashes
279      _placePanes
280    }
281    default {
282      error "Invalid option for -showhandle: $itk_option(-showhandle).\
283        Must be 1 or 0."
284    }
285  }
286}
287
288
289# ------------------------------------------------------------------
290# OPTION: -thickness
291#
292# Specifies the thickness of the separators.  It sets the width and
293# height of the separator to the thickness value and the borderwidth
294# to half the thickness.
295# ------------------------------------------------------------------
296itcl::configbody iwidgets::Panedwindow::thickness {
297    set pixels [winfo pixels $itk_component(hull) \
298	    $itk_option(-thickness)]
299    set itk_option(-thickness) $pixels
300
301    if {$_initialized} {
302	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
303	    $itk_component(separator$i) configure \
304		    -height $itk_option(-thickness)
305	    $itk_component(separator$i) configure \
306		    -width $itk_option(-thickness)
307	    $itk_component(separator$i) configure \
308		    -borderwidth [expr {$itk_option(-thickness) / 2}]
309	}
310
311	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
312	    _placeSash $i
313	}
314    }
315}
316
317# ------------------------------------------------------------------
318# OPTION: -sashindent
319#
320# Specifies the placement of the sash along the panes.  A positive
321# value causes the sash to be offset from the near (left/top) side
322# of the pane, and a negative value causes the sash to be offset from
323# the far (right/bottom) side.  If the offset is greater than the
324# width, then the sash is placed flush against the side.
325# ------------------------------------------------------------------
326itcl::configbody iwidgets::Panedwindow::sashindent {
327    set pixels [winfo pixels $itk_component(hull) \
328	    $itk_option(-sashindent)]
329    set itk_option(-sashindent) $pixels
330
331    if {$_initialized} {
332	for {set i 1} {$i < [llength $_activePanes]} {incr i} {
333	    _placeSash $i
334	}
335    }
336}
337
338# ------------------------------------------------------------------
339#                            METHODS
340# ------------------------------------------------------------------
341
342# ------------------------------------------------------------------
343# METHOD: index index
344#
345# Searches the panes in the paned window for the one with the
346# requested tag, numerical index, or keyword "end".  Returns the pane's
347# numerical index if found, otherwise error.
348# ------------------------------------------------------------------
349itcl::body iwidgets::Panedwindow::index {index} {
350    if {[llength $_panes] > 0} {
351	if {[regexp {(^[0-9]+$)} $index]} {
352	    if {$index < [llength $_panes]} {
353		return $index
354	    } else {
355		error "Panedwindow index \"$index\" is out of range"
356	    }
357
358	} elseif {$index == "end"} {
359	    return [expr {[llength $_panes] - 1}]
360
361	} else {
362	    if {[set idx [lsearch $_panes $index]] != -1} {
363		return $idx
364	    }
365
366	    error "bad Panedwindow index \"$index\": must be number, end,\
367		    or pattern"
368	}
369
370    } else {
371	error "Panedwindow \"$itk_component(hull)\" has no panes"
372    }
373}
374
375# ------------------------------------------------------------------
376# METHOD: childsite ?index?
377#
378# Given an index return the specifc childsite path name.  Invoked
379# without an index return a list of all the child site panes.  The
380# list is ordered from the near side (left/top).
381# ------------------------------------------------------------------
382itcl::body iwidgets::Panedwindow::childsite {args} {
383    if {! $_initialized} {
384	set _initialized 1
385	reset
386    }
387
388    if {[llength $args] == 0} {
389	set children {}
390
391	foreach pane $_panes {
392	    lappend children [$itk_component($pane) childSite]
393	}
394
395	return $children
396
397    } else {
398	set index [index [lindex $args 0]]
399	return [$itk_component([lindex $_panes $index]) childSite]
400    }
401}
402
403# ------------------------------------------------------------------
404# METHOD: fraction percentage percentage ?percentage ...?
405#
406# Sets the visible percentage of the panes.  Specifies a list of
407# percentages which are applied to the currently visible panes from
408# the near side (left/top).  The number of percentages must be equal
409# to the current number of visible (mapped) panes and add up to 100.
410# ------------------------------------------------------------------
411itcl::body iwidgets::Panedwindow::fraction {args} {
412    #set args [linsert $args 0 $percentage1 $percentage2]
413
414
415    if {[llength $args] == [llength $_activePanes]} {
416	set sum 0
417
418	for {set i 0} {$i < [llength $args]} {incr i} {
419	    set sum [expr {$sum + [lindex $args $i]}]
420	}
421
422	if {$sum == 100} {
423	    set perc 0.0
424
425	    for {set i 0} {$i < [llength $_activePanes]} {incr i} {
426		set _frac($i) $perc
427		set perc [expr {$perc + [expr {[lindex $args $i] / 100.0}]}]
428	    }
429
430	    set _frac($i) 1.0
431
432	    if {[winfo ismapped $itk_component(hull)]} {
433		_placePanes
434	    }
435
436	} else {
437	    error "bad fraction arguments \"$args\": they should add\
438		    up to 100"
439	}
440
441    } elseif {[llength $args] == 0} {
442
443        for {set i 0; set j 1} {$j < [llength $_activePanes]} {incr i; incr j} {
444            lappend _ret [expr {round(($_frac($j) - $_frac($i))*100)}]
445        }
446	lappend _ret [eval expr {100 - ([join $_ret +])}]
447
448        return $_ret
449    } else {
450	error "wrong # args: should be \"$itk_component(hull)\
451		fraction percentage percentage ?percentage ...?\",\
452		where the number of percentages is\
453		[llength $_activePanes] and equal 100
454		or \"$itk_component(hull) fraction\"
455		which will return a list of the current percentages"
456    }
457}
458
459# ------------------------------------------------------------------
460# METHOD: add tag ?option value option value ...?
461#
462# Add a new pane to the paned window to the far (right/bottom) side.
463# The method takes additional options which are passed on to the
464# pane constructor.  These include -margin, and -minimum.  The path
465# of the pane is returned.
466# ------------------------------------------------------------------
467itcl::body iwidgets::Panedwindow::add {tag args} {
468    #
469    # Create panes.
470    #
471    itk_component add $tag {
472	eval iwidgets::Pane $itk_interior.pane[incr _unique] $args
473    } {
474	keep -background -cursor
475    }
476
477    lappend _panes $tag
478    lappend _activePanes $tag
479
480    reset
481
482    return $itk_component($tag)
483}
484
485# ------------------------------------------------------------------
486# METHOD: insert index tag ?option value option value ...?
487#
488# Insert the specified pane in the paned window just before the one
489# given by index.  Any additional options which are passed on to the
490# pane constructor.  These include -margin, -minimum.  The path of
491# the pane is returned.
492# ------------------------------------------------------------------
493itcl::body iwidgets::Panedwindow::insert {index tag args} {
494    #
495    # Create panes.
496    #
497    itk_component add $tag {
498	eval iwidgets::Pane $itk_interior.pane[incr _unique] $args
499    } {
500	keep -background -cursor
501    }
502
503    set index [index $index]
504    set _panes [linsert $_panes $index $tag]
505    lappend _activePanes $tag
506
507    reset
508
509    return $itk_component($tag)
510}
511
512# ------------------------------------------------------------------
513# METHOD: delete index
514#
515# Delete the specified pane.
516# ------------------------------------------------------------------
517itcl::body iwidgets::Panedwindow::delete {index} {
518    set index [index $index]
519    set tag [lindex $_panes $index]
520
521    destroy $itk_component($tag)
522
523    set _panes [lreplace $_panes $index $index]
524
525    reset
526}
527
528# ------------------------------------------------------------------
529# METHOD: hide index
530#
531# Remove the specified pane from the paned window.
532# ------------------------------------------------------------------
533itcl::body iwidgets::Panedwindow::hide {index} {
534    set index [index $index]
535    set tag [lindex $_panes $index]
536
537    if {[set idx [lsearch -exact $_activePanes $tag]] != -1} {
538	set _activePanes [lreplace $_activePanes $idx $idx]
539    }
540
541    reset
542}
543
544# ------------------------------------------------------------------
545# METHOD: show index
546#
547# Display the specified pane in the paned window.
548# ------------------------------------------------------------------
549itcl::body iwidgets::Panedwindow::show {index} {
550    set index [index $index]
551    set tag [lindex $_panes $index]
552
553    if {[lsearch -exact $_activePanes $tag] == -1} {
554	lappend _activePanes $tag
555    }
556
557    reset
558}
559
560# ------------------------------------------------------------------
561# METHOD: paneconfigure index ?option? ?value option value ...?
562#
563# Configure a specified pane.  This method allows configuration of
564# panes from the Panedwindow level.  The options may have any of the
565# values accepted by the add method.
566# ------------------------------------------------------------------
567itcl::body iwidgets::Panedwindow::paneconfigure {index args} {
568    set index [index $index]
569    set tag [lindex $_panes $index]
570
571    return [uplevel $itk_component($tag) configure $args]
572}
573
574# ------------------------------------------------------------------
575# METHOD: reset
576#
577# Redisplay the panes based on the default percentages of the panes.
578# ------------------------------------------------------------------
579itcl::body iwidgets::Panedwindow::reset {} {
580    if {$_initialized && [llength $_panes]} {
581	_setActivePanes
582	_setFracArray
583
584	_makeSashes
585	_placePanes
586    }
587}
588
589# ------------------------------------------------------------------
590# PROTECTED METHOD: _pwConfigureEventHandler
591#
592# Performs operations necessary following a configure event.  This
593# includes placing the panes.
594# ------------------------------------------------------------------
595itcl::body iwidgets::Panedwindow::_pwConfigureEventHandler {width height} {
596    set _width $width
597    set _height $height
598    if {$_initialized} {
599	_placePanes
600    } else {
601	set _initialized 1
602	reset
603    }
604}
605
606# ------------------------------------------------------------------
607# PROTECTED METHOD: _startGrip where num
608#
609# Starts the sash drag and drop operation.  At the start of the drag
610# operation all the information is known as for the upper and lower
611# limits for sash movement.  The calculation is made at this time and
612# stored in protected variables for later access during the drag
613# handling routines.
614# ------------------------------------------------------------------
615itcl::body iwidgets::Panedwindow::_startGrip {where num} {
616    if {$itk_option(-orient) == "horizontal"} {
617      set _dimension $_height
618    } else {
619      set _dimension $_width
620    }
621
622    set _minsashmoved $num
623    set _maxsashmoved $num
624    set totMinHeight 0
625    set cnt [llength $_activePanes]
626    set _sashloc(0) 0
627    set _pixels($cnt) [expr {int($_dimension)}]
628    for {set i 0} {$i < $cnt} {incr i} {
629      set _pixels($i) [expr {int($_frac($i) * $_dimension)}]
630      set margaft [$itk_component([lindex $_activePanes $i]) cget -margin]
631      set minaft [$itk_component([lindex $_activePanes $i]) cget -minimum]
632      set _minheight($i) [expr {$minaft + (2 * $margaft)}]
633      incr totMinHeight $_minheight($i)
634    }
635    set _dragging [expr {$_dimension > $totMinHeight}]
636
637    grab  $itk_component(sash$num)
638    raise $itk_component(separator$num)
639    raise $itk_component(sash$num)
640
641    $itk_component(sash$num) configure -relief sunken
642}
643
644# ------------------------------------------------------------------
645# PROTECTED METHOD: _endGrip where num
646#
647# Ends the sash drag and drop operation.
648# ------------------------------------------------------------------
649itcl::body iwidgets::Panedwindow::_endGrip {where num} {
650    $itk_component(sash$num) configure -relief $_relief($itk_option(-showhandle))
651    grab release $itk_component(sash$num)
652    if {$_dragging} {
653      _calcFraction [expr {$_sashloc($num) + $where}] $num
654      _placePanes [expr {$_minsashmoved - 1}] $_maxsashmoved
655      set _dragging 0
656    }
657}
658
659# ------------------------------------------------------------------
660# PROTECTED METHOD: _configGrip where num
661#
662# Configure  action for sash.
663# ------------------------------------------------------------------
664itcl::body iwidgets::Panedwindow::_configGrip {where num} {
665   set _sashloc($num) $where
666}
667
668# ------------------------------------------------------------------
669# PROTECTED METHOD: _handleGrip where num
670#
671# Motion action for sash.
672# ------------------------------------------------------------------
673itcl::body iwidgets::Panedwindow::_handleGrip {where num} {
674 if {$_dragging} {
675  _moveSash [expr {$where + $_sashloc($num)}] $num
676  incr _movecount
677  if {$_movecount>4} {
678    set _movecount 0
679    update idletasks
680  }
681 }
682}
683
684# ------------------------------------------------------------------
685# PROTECTED METHOD: _moveSash where num
686#
687# Move the sash to the absolute pixel location
688# ------------------------------------------------------------------
689itcl::body iwidgets::Panedwindow::_moveSash {where num} {
690  set _minsashmoved [expr {($_minsashmoved<$num)?$_minsashmoved:$num}]
691  set _maxsashmoved [expr {($_maxsashmoved>$num)?$_maxsashmoved:$num}]
692  set oldfrac $_frac($num)
693  _calcFraction $where $num
694  if {$_frac($num)!=$oldfrac} { _placeSash $num }
695}
696
697# ------------------------------------------------------------------
698# PRIVATE METHOD: _setFracArray
699#
700# Calculates the percentages for the fraction array which lists the
701# percentages for each pane.
702# ------------------------------------------------------------------
703itcl::body iwidgets::Panedwindow::_setFracArray {} {
704    set perc 0.0
705    if {[llength $_activePanes] != 0} {
706	set percIncr [expr {1.0 / [llength $_activePanes]}]
707    }
708
709    for {set i 0} {$i < [llength $_activePanes]} {incr i} {
710	set _frac($i) $perc
711	set perc [expr {$perc + $percIncr}]
712    }
713
714    set _frac($i) 1.0
715}
716
717# ------------------------------------------------------------------
718# PRIVATE METHOD: _setActivePanes
719#
720# Resets the active pane list.
721# ------------------------------------------------------------------
722itcl::body iwidgets::Panedwindow::_setActivePanes {} {
723    set _prevActivePanes $_activePanes
724
725    set _activePanes {}
726
727    foreach pane $_panes {
728	if {[lsearch -exact $_prevActivePanes $pane] != -1} {
729	    lappend _activePanes $pane
730	}
731    }
732}
733
734# ------------------------------------------------------------------
735# PRIVATE METHOD: _calcFraction where num
736#
737# Determines the fraction for the sash.  Make sure the fraction does
738# not go past the minimum for the pane on each side of the separator.
739# ------------------------------------------------------------------
740itcl::body iwidgets::Panedwindow::_calcFraction {where num} {
741
742    set numi [expr {$num + 1}]
743    set numd [expr {$num - 1}]
744
745    set _lowerlimit [expr {$_pixels($numd) + $_minheight($numd)}]
746    set _upperlimit [expr {$_pixels($numi) - $_minheight($num)}]
747
748    set dir [expr {$where - $_pixels($num)}]
749
750    if {$where < $_lowerlimit && $dir <= 0} {
751     if {$num == 1} {
752      set _pixels($num) $_lowerlimit
753     } {
754      _moveSash [expr {$where - $_minheight($numd)}] $numd
755      set _pixels($num) [expr {$_pixels($numd) + $_minheight($numd)}]
756     }
757    } elseif {$where > $_upperlimit && $dir >= 0} {
758     if {$numi == [llength $_activePanes]} {
759      set _pixels($num) $_upperlimit
760     } {
761      _moveSash [expr {$where + $_minheight($num)}] $numi
762      set _pixels($num) \
763	     [expr {$_pixels($numi) - $_minheight($num)}]
764     }
765    } else {
766      set _pixels($num) $where
767    }
768    set _frac($num) [expr $_pixels($num).0 / $_dimension]
769}
770
771# ------------------------------------------------------------------
772# PRIVATE METHOD: _makeSashes
773#
774# Removes any previous sashes and separators and creates new one.
775# ------------------------------------------------------------------
776itcl::body iwidgets::Panedwindow::_makeSashes {} {
777    #
778    # Remove any existing sashes and separators.
779    #
780    foreach sash $_sashes {
781	destroy $itk_component($sash)
782    }
783
784    foreach separator $_separators {
785	destroy $itk_component($separator)
786    }
787
788    set _sashes {}
789    set _separators {}
790
791    #
792    # Create one less separator and sash than the number of panes.
793    #
794    for {set id 1} {$id < [llength $_activePanes]} {incr id} {
795	itk_component add sash$id {
796	    frame $itk_interior.sash$id -relief $_relief($itk_option(-showhandle)) \
797		    -borderwidth $itk_option(-sashborderwidth) \
798		    -cursor $itk_option(-sashcursor) \
799		    -width $itk_option(-sashwidth) \
800		    -height $itk_option(-sashheight)
801	} {
802	    keep -background
803	}
804
805	lappend _sashes sash$id
806
807	switch $itk_option(-orient) {
808	    vertical {
809		bind $itk_component(sash$id) <Button-1> \
810			[itcl::code $this _startGrip %x $id]
811		bind $itk_component(sash$id) <B1-Motion> \
812			[itcl::code $this _handleGrip %x $id]
813		bind $itk_component(sash$id) <B1-ButtonRelease-1> \
814			[itcl::code $this _endGrip %x $id]
815		bind $itk_component(sash$id) <Configure> \
816			    [itcl::code $this _configGrip %x $id]
817	    }
818
819	    horizontal {
820		bind $itk_component(sash$id) <Button-1> \
821			[itcl::code $this _startGrip %y $id]
822		bind $itk_component(sash$id) <B1-Motion> \
823			[itcl::code $this _handleGrip %y $id]
824		bind $itk_component(sash$id) <B1-ButtonRelease-1> \
825			[itcl::code $this _endGrip %y $id]
826		bind $itk_component(sash$id) <Configure> \
827			    [itcl::code $this _configGrip %y $id]
828	    }
829	}
830
831	itk_component add separator$id {
832	    frame $itk_interior.separator$id -relief sunken \
833		    -height $itk_option(-thickness) \
834		    -width $itk_option(-thickness) \
835		    -borderwidth [expr {$itk_option(-thickness) / 2}]
836	} {
837	    keep -background -cursor
838	}
839
840	lappend _separators separator$id
841    }
842}
843
844# ------------------------------------------------------------------
845# PRIVATE METHOD: _placeSash i
846#
847# Places the position of the sash and separator.
848# ------------------------------------------------------------------
849itcl::body iwidgets::Panedwindow::_placeSash {i} {
850    if {$itk_option(-orient) == "horizontal"} {
851	place $itk_component(separator$i) -in $itk_component(hull) \
852		-x 0 -relwidth 1 -rely $_frac($i) -anchor w \
853		-height $itk_option(-thickness)
854
855	if {$itk_option(-sashindent) < 0} {
856	    set sashPos [expr {$_width + $itk_option(-sashindent)}]
857	    set sashAnchor e
858	} else {
859	    set sashPos $itk_option(-sashindent)
860	    set sashAnchor w
861	}
862
863	if {$itk_option(-showhandle)} {
864		place $itk_component(sash$i) -in $itk_component(hull) \
865			-x $sashPos -rely $_frac($i) -anchor $sashAnchor
866	} else {
867		place $itk_component(sash$i) -in $itk_component(hull) \
868			-x 0 -relwidth 1 -rely $_frac($i) -anchor w \
869			-height $itk_option(-thickness)
870	}
871
872    } else {
873	place $itk_component(separator$i) -in $itk_component(hull) \
874		-y 0 -relheight 1 -relx $_frac($i) -anchor n \
875		-width $itk_option(-thickness)
876
877	if {$itk_option(-sashindent) < 0} {
878	    set sashPos [expr {$_height + $itk_option(-sashindent)}]
879	    set sashAnchor s
880	} else {
881	    set sashPos $itk_option(-sashindent)
882	    set sashAnchor n
883	}
884
885	if {$itk_option(-showhandle)} {
886
887		place $itk_component(sash$i) -in $itk_component(hull) \
888			-y $sashPos -relx $_frac($i) -anchor $sashAnchor
889	} else {
890		place $itk_component(sash$i) -in $itk_component(hull) \
891			-y 0 -relheight 1 -relx $_frac($i) -anchor n \
892			-width $itk_option(-thickness)
893	}
894    }
895}
896
897# ------------------------------------------------------------------
898# PRIVATE METHOD: _placePanes
899#
900# Resets the panes of the window following movement of the sash.
901# ------------------------------------------------------------------
902itcl::body iwidgets::Panedwindow::_placePanes {{start 0} {end end}} {
903     if {$end=="end"} { set end [expr {[llength $_activePanes] - 1}] }
904     set _updatePanes [lrange $_activePanes $start $end]
905     if {$_updatePanes == $_activePanes} {
906       set _forgetPanes $_panes
907     } {
908       set _forgetPanes $_updatePanes
909     }
910    foreach pane $_forgetPanes {
911	place forget $itk_component($pane)
912    }
913
914
915    if {$itk_option(-orient) == "horizontal"} {
916	set i $start
917	foreach pane $_updatePanes {
918	    place $itk_component($pane) -in $itk_component(hull) \
919		    -x 0 -rely $_frac($i) -relwidth 1 \
920		    -relheight [expr {$_frac([expr {$i + 1}]) - $_frac($i)}]
921	    incr i
922	}
923
924    } else {
925	set i $start
926	foreach pane $_updatePanes {
927	    place $itk_component($pane) -in $itk_component(hull) \
928		    -y 0 -relx $_frac($i) -relheight 1 \
929		    -relwidth [expr {$_frac([expr {$i + 1}]) - $_frac($i)}]
930	    incr i
931	}
932
933    }
934
935    for {set i [expr {$start+1}]} {$i <= $end} {incr i} {
936	if {[array names itk_component separator$i] != ""} {
937	    _placeSash $i
938	    raise $itk_component(separator$i)
939	    raise $itk_component(sash$i)
940	}
941    }
942}
943