OSDN Git Service

2002-12-03 Martin M. Hunt <hunt@redhat.com>
[pf3gnuchains/pf3gnuchains3x.git] / libgui / library / panedwindow.tcl
1 #
2 # Panedwindow  
3 # ----------------------------------------------------------------------
4 # Implements a very general panedwindow which allows for mixing resizable
5 # and non-resizable panes.  It also allows limits to be set on individual
6 # pane sizes, both minimum and maximum.
7 #
8 # The look of this widget is much like Window, instead of the Motif-like
9 # iwidget panedwindow.
10 # ----------------------------------------------------------------------
11
12 # Portions of this code are originally from the iwidget panedwindow which
13 # is Copyright (c) 1995 DSC Technologies Corporation 
14
15 itk::usual PanedWindow {
16   keep -background -cursor
17 }
18
19 # ------------------------------------------------------------------
20 #                            PANEDWINDOW
21 # ------------------------------------------------------------------
22 itcl::class cyg::PanedWindow {
23   inherit itk::Widget
24
25   constructor {args} {}
26
27   itk_option define -orient orient Orient horizontal
28   itk_option define -sashwidth sashWidth SashWidth 10
29   itk_option define -sashcolor sashColor SashColor gray
30
31   public {
32     method index {index}
33     method childsite {args}
34     method add {tag args}
35     method insert {index tag args}
36     method delete {index}
37     method hide {index}
38     method replace {pane1 pane2}
39     method show {index}
40     method paneconfigure {index args}
41     method reset {}
42   }
43
44   private {
45     method _eventHandler {width height}
46     method _startDrag {num}
47     method _endDrag {where num}
48     method _configDrag {where num}
49     method _handleDrag {where num}
50     method _moveSash {where num {dir ""}}
51
52     method _resizeArray {}
53     method _setActivePanes {}
54     method _calcPos {where num {dir ""}}
55     method _makeSashes {}
56     method _placeSash {i}
57     method _placePanes {{start 0} {end end} {forget 0}}
58
59     variable _initialized 0     ;# flag set when widget is first configured
60     variable _sashes {}         ;# List of sashes.
61
62     # Pane information
63     variable _panes {}          ;# List of panes.
64     variable _activePanes {}    ;# List of active panes.
65     variable _where             ;# Array of relative positions
66     variable _ploc              ;# Array of pixel positions
67     variable _frac              ;# Array of relative pane sizes
68     variable _pixels            ;# Array of sizes in pixels for non-resizable panes
69     variable _max               ;# Array of pane maximum locations
70     variable _min               ;# Array of pane minimum locations
71     variable _pmin              ;# Array of pane minimum size
72     variable _pmax              ;# Array of pane maximum size
73
74     variable _dimension 0       ;# width or height of window
75     variable _dir "height"      ;# resizable direction, "height" or "width"
76     variable _rPixels
77
78     variable _sashloc          ;# Array of dist of sash from above/left.
79
80     variable _minsashmoved     ;# Lowest sash moved during dragging.
81     variable _maxsashmoved     ;# Highest sash moved during dragging.
82
83     variable _width 0           ;# hull's width.
84     variable _height 0          ;# hull's height.
85     variable _unique -1         ;# Unique number for pane names.
86   }
87 }
88
89 #
90 # Provide a lowercased access method for the PanedWindow class.
91
92 proc ::cyg::panedwindow {pathName args} {
93   uplevel ::cyg::PanedWindow $pathName $args
94 }
95
96 #
97 # Use option database to override default resources of base classes.
98 #
99 option add *PanedWindow.width 10 widgetDefault
100 option add *PanedWindow.height 10 widgetDefault
101
102 # ------------------------------------------------------------------
103 #                        CONSTRUCTOR
104 # ------------------------------------------------------------------
105 itcl::body cyg::PanedWindow::constructor {args} {
106   itk_option add hull.width hull.height
107
108   pack propagate $itk_component(hull) no
109   
110   bind pw-config-$this <Configure> [code $this _eventHandler %w %h]
111   bindtags $itk_component(hull) \
112     [linsert [bindtags $itk_component(hull)] 0 pw-config-$this]
113   
114   eval itk_initialize $args
115 }
116
117 # ------------------------------------------------------------------
118 #                             OPTIONS
119 # ------------------------------------------------------------------
120
121 # ------------------------------------------------------------------
122 # OPTION: -orient
123 #
124 # Specifies the orientation of the sashes.  Once the paned window
125 # has been mapped, set the sash bindings and place the panes.
126 # ------------------------------------------------------------------
127 itcl::configbody cyg::PanedWindow::orient {
128   #puts "orient $_initialized"
129   if {$_initialized} {
130     set orient $itk_option(-orient)
131     if {$orient != "vertical" && $orient != "horizontal"} {
132       error "bad orientation option \"$itk_option(-orient)\":\
133                 should be horizontal or vertical"
134     }
135     if {[string compare $orient "vertical"]} {
136       set _dimension $_height
137       set _dir "height"
138     } else {
139       set _dimension $_width
140       set _dir "width"
141     }
142     _resizeArray
143     _makeSashes
144     _placePanes 0 end 1
145   }
146 }
147
148 # ------------------------------------------------------------------
149 # OPTION: -sashwidth
150 #
151 # Specifies the width of the sash.
152 # ------------------------------------------------------------------
153 itcl::configbody cyg::PanedWindow::sashwidth {
154   set pixels [winfo pixels $itk_component(hull) $itk_option(-sashwidth)]
155   set itk_option(-sashwidth) $pixels
156   
157   if {$_initialized} {
158     # FIXME
159     for {set i 1} {$i < [llength $_panes]} {incr i} {
160       $itk_component(sash$i) configure \
161         -width $itk_option(-sashwidth) -height $itk_option(-sashwidth) \
162         -borderwidth 2
163     }
164     for {set i 1} {$i < [llength $_panes]} {incr i} {
165       _placeSash $i
166     }
167   }
168 }
169
170 # ------------------------------------------------------------------
171 # OPTION: -sashcolor
172 #
173 # Specifies the color of the sash.
174 # ------------------------------------------------------------------
175 itcl::configbody cyg::PanedWindow::sashcolor {
176   if {$_initialized} {
177     for {set i 1} {$i < [llength $_panes]} {incr i} {
178       $itk_component(sash$i) configure -background $itk_option(-sashcolor)
179     }
180   }
181 }
182
183 # ------------------------------------------------------------------
184 #                            METHODS
185 # ------------------------------------------------------------------
186
187 # ------------------------------------------------------------------
188 # METHOD: index index
189 #
190 # Searches the panes in the paned window for the one with the 
191 # requested tag, numerical index, or keyword "end".  Returns the pane's 
192 # numerical index if found, otherwise error.
193 # ------------------------------------------------------------------    
194 itcl::body cyg::PanedWindow::index {index} {
195   if {[llength $_panes] > 0} {
196     if {[regexp {(^[0-9]+$)} $index]} {
197       if {$index < [llength $_panes]} {
198         return $index
199       } else {
200         error "PanedWindow index \"$index\" is out of range"
201       }
202     } elseif {$index == "end"} {
203       return [expr [llength $_panes] - 1]
204     } else {
205       if {[set idx [lsearch $_panes $index]] != -1} {
206         return $idx
207       }
208       error "bad PanedWindow index \"$index\": must be number, end,\
209                     or pattern"
210     }
211   } else {
212     error "PanedWindow \"$itk_component(hull)\" has no panes"
213   }
214 }
215
216 # ------------------------------------------------------------------
217 # METHOD: childsite ?index?
218 #
219 # Given an index return the specifc childsite path name.  Invoked 
220 # without an index return a list of all the child site panes.  The 
221 # list is ordered from the near side (left/top).
222 # ------------------------------------------------------------------
223 itcl::body cyg::PanedWindow::childsite {args} {
224   #puts "childsite $args ($_initialized)"
225   
226   if {[llength $args] == 0} {
227     set children {}
228     foreach pane $_panes {
229       lappend children [$itk_component($pane) childSite]
230     }
231     return $children
232   } else {
233     set index [index [lindex $args 0]]
234     return [$itk_component([lindex $_panes $index]) childSite]
235   }
236 }
237
238
239 # ------------------------------------------------------------------
240 # METHOD: add tag ?option value option value ...?
241 #
242 # Add a new pane to the paned window to the far (right/bottom) side.
243 # The method takes additional options which are passed on to the 
244 # pane constructor.  These include -margin, and -minimum.  The path 
245 # of the pane is returned.
246 # ------------------------------------------------------------------
247 itcl::body cyg::PanedWindow::add {tag args} {
248   itk_component add $tag {
249     eval cyg::Pane $itk_interior.pane[incr _unique] $args
250   } {
251     keep -background -cursor
252   }
253   
254   lappend _panes $tag
255   lappend _activePanes $tag
256   reset  
257   return $itk_component($tag)
258 }
259
260 # ------------------------------------------------------------------
261 # METHOD: insert index tag ?option value option value ...?
262 #
263 # Insert the specified pane in the paned window just before the one 
264 # given by index.  Any additional options which are passed on to the 
265 # pane constructor.  These include -margin, -minimum.  The path of 
266 # the pane is returned.
267 # ------------------------------------------------------------------
268 itcl::body cyg::PanedWindow::insert {index tag args} {
269   itk_component add $tag {
270     eval cyg::Pane $itk_interior.pane[incr _unique] $args
271   } {
272     keep -background -cursor
273   }
274   
275   set index [index $index]
276   set _panes [linsert $_panes $index $tag]
277   lappend _activePanes $tag  
278   reset
279   return $itk_component($tag)
280 }
281
282 # ------------------------------------------------------------------
283 # METHOD: delete index
284 #
285 # Delete the specified pane.
286 # ------------------------------------------------------------------
287 itcl::body cyg::PanedWindow::delete {index} {
288   set index [index $index]
289   set tag [lindex $_panes $index]
290
291   # remove the itk component
292   destroy $itk_component($tag)
293   # remove it from panes list
294   set _panes [lreplace $_panes $index $index]  
295   
296   # remove its _frac value
297   set ind [lsearch -exact $_activePanes $tag]
298   if {$ind != -1 && [info exists _frac($ind)]} {
299     unset _frac($ind)
300   }
301   
302   # this will reset _activePane and resize things
303   reset
304 }
305
306 # ------------------------------------------------------------------
307 # METHOD: hide index
308 #
309 # Remove the specified pane from the paned window. 
310 # ------------------------------------------------------------------
311 itcl::body cyg::PanedWindow::hide {index} {
312   set index [index $index]
313   set tag [lindex $_panes $index]
314   
315   if {[set idx [lsearch -exact $_activePanes $tag]] != -1} {
316     set _activePanes [lreplace $_activePanes $idx $idx]
317     if {[info exists _frac($idx)]} {unset _frac($idx)}
318   }
319
320   reset
321 }
322
323 itcl::body cyg::PanedWindow::replace {pane1 pane2} {
324   set ind1 [lsearch -exact $_activePanes $pane1]
325   if {$ind1 == -1} {
326     error "$pane1 is not an active pane name."
327   }
328   set ind2 [lsearch -exact $_panes $pane2]
329   if {$ind2 == -1} {
330     error "Pane $pane2 does not exist."
331   }
332   set _activePanes [lreplace $_activePanes $ind1 $ind1 $pane2]
333   _placePanes 0 $ind1 1
334 }
335
336 # ------------------------------------------------------------------
337 # METHOD: show index
338 #
339 # Display the specified pane in the paned window.
340 # ------------------------------------------------------------------
341 itcl::body cyg::PanedWindow::show {index} {
342   set index [index $index]
343   set tag [lindex $_panes $index]
344   
345   if {[lsearch -exact $_activePanes $tag] == -1} {
346     lappend _activePanes $tag
347   }
348
349   reset
350 }
351
352 # ------------------------------------------------------------------
353 # METHOD: paneconfigure index ?option? ?value option value ...?
354 #
355 # Configure a specified pane.  This method allows configuration of
356 # panes from the PanedWindow level.  The options may have any of the 
357 # values accepted by the add method.
358 # ------------------------------------------------------------------
359 itcl::body cyg::PanedWindow::paneconfigure {index args} {
360   set index [index $index]
361   set tag [lindex $_panes $index]
362   return [uplevel $itk_component($tag) configure $args]
363 }
364
365 # ------------------------------------------------------------------
366 # METHOD: reset
367 #
368 # Redisplay the panes based on the default percentages of the panes.
369 # ------------------------------------------------------------------
370 itcl::body cyg::PanedWindow::reset {} {
371   if {$_initialized && [llength $_panes]} {
372     #puts RESET
373     _setActivePanes
374     _resizeArray
375     _makeSashes
376     _placePanes 0 end 1
377   }
378 }
379
380 # ------------------------------------------------------------------
381 # PRIVATE METHOD: _setActivePanes
382 #
383 # Resets the active pane list.
384 # ------------------------------------------------------------------
385 itcl::body cyg::PanedWindow::_setActivePanes {} {
386   set _prevActivePanes $_activePanes
387   set _activePanes {}
388   
389   foreach pane $_panes {
390     if {[lsearch -exact $_prevActivePanes $pane] != -1} {
391       lappend _activePanes $pane
392     }
393   }
394 }
395
396 # ------------------------------------------------------------------
397 # PROTECTED METHOD: _eventHandler
398 #
399 # Performs operations necessary following a configure event.  This
400 # includes placing the panes.
401 # ------------------------------------------------------------------
402 itcl::body cyg::PanedWindow::_eventHandler {width height} {
403   #puts "Event $width $height"
404   set _width $width
405   set _height $height
406   if {[string compare $itk_option(-orient) "vertical"]} {
407     set _dimension $_height
408     set _dir "height"
409   } else {
410     set _dimension $_width
411     set _dir "width"
412   }
413   
414   if {$_initialized} {
415     _resizeArray
416     _placePanes
417   } else {
418     set _initialized 1
419     reset
420   }
421 }
422
423 # ------------------------------------------------------------------
424 # PRIVATE METHOD: _resizeArray
425 #
426 # Recalculates the sizes and positions of all the panes.
427 # This is only done at startup, when the window size changes, when
428 # a new pane is added, or the orientation is changed.
429 #
430 # _frac($i) contains:
431 #               % of resizable space when pane$i is resizable
432 # _pixels($i) contains
433 #               pixels when pane$i is not resizable
434 #
435 # _where($i) contains the relative position of the top of pane$i
436 # ------------------------------------------------------------------
437 itcl::body cyg::PanedWindow::_resizeArray {} {
438   set numpanes 0
439   set _rPixels 0
440   set totalFrac 0.0
441   set numfreepanes 0
442
443   #puts "sresizeArray dim=$_dimension dir=$_dir"
444
445   # first pass. Count the number of resizable panes and
446   # the pixels reserved for non-resizable panes.
447   set i 0
448   foreach p $_activePanes {
449     set _resizable($i) [$itk_component($p) cget -resizable]
450     if {$_resizable($i)} {
451       # remember pane min and max
452       set _pmin($i) [$itk_component($p) cget -minimum]
453       set _pmax($i) [$itk_component($p) cget -maximum]
454
455       incr numpanes
456       if {[info exists _frac($i)]} {
457         # sum up all the percents
458         set totalFrac [expr $totalFrac + $_frac($i)]
459       } else {
460         # number of new windows not yet sized
461         incr numfreepanes
462       }
463     } else {
464       set _pixels($i) [winfo req$_dir $itk_component($p)]
465       set _pmin($i) $_pixels($i)
466       set _pmax($i) $_pixels($i)
467       incr _rPixels $_pixels($i)
468     }
469     incr i
470   }
471   set totalpanes $i
472
473   #puts "numpanes=$numpanes nfp=$numfreepanes _rPixels=$_rPixels  totalFrac=$totalFrac"
474
475   if {$numfreepanes} {
476     # set size for the new window(s) to average size
477     if {$totalFrac > 0.0} {
478       set freepanesize [expr $totalFrac / ($numpanes - $numfreepanes)]
479     } else {
480       set freepanesize [expr 1.0 / $numpanes.0]
481     }
482     for {set i 0} {$i < $totalpanes} {incr i} {
483       if {$_resizable($i) && ![info exists _frac($i)]} {
484         set _frac($i) $freepanesize
485         set totalFrac [expr $totalFrac + $_frac($i)]
486       }
487     }
488   }
489
490   set done 0
491
492   while {!$done} {
493     # force to a reasonable value
494     if {$totalFrac <= 0.0} { set totalFrac 1.0 }
495
496     # scale the _frac array
497     if {$totalFrac > 1.01 || $totalFrac < 0.99} {
498       set cor [expr 1.0 / $totalFrac]
499       set totalFrac 0.0
500       for {set i 0} {$i < $totalpanes} {incr i} {
501         if {$_resizable($i)} {
502           set _frac($i) [expr $_frac($i) * $cor]
503           set totalFrac [expr $totalFrac + $_frac($i)]
504         }
505       }
506     }
507     
508     # bounds checking; look for panes that are too small or too large
509     # if one is found, fix its size at the min or max and mark the
510     # window non-resizable. Adjust percents and try again.
511     set done 1
512     for {set i 0} {$i < $totalpanes} {incr i} {
513       if {$_resizable($i)} {
514         set _pixels($i) [expr int($_frac($i) * ($_dimension - $_rPixels.0))]
515         if {$_pixels($i) < $_pmin($i)} {
516           set _resizable($i) 0
517           set totalFrac [expr $totalFrac - $_frac($i)]
518           set _pixels($i) $_pmin($i)
519           incr  _rPixels $_pixels($i)
520           set done 0
521           break
522         } elseif {$_pmax($i) && $_pixels($i) > $_pmax($i)} {
523           set _resizable($i) 0
524           set totalFrac [expr $totalFrac - $_frac($i)]
525           set _pixels($i) $_pmax($i)
526           incr  _rPixels $_pixels($i)
527           set done 0
528           break
529         }
530       }
531     }
532   }
533
534   # Done adjusting. Now build pane position arrays.  These are designed
535   # to minimize calculations while resizing.
536   # Note: position of sash $i = position of top of pane $i
537   # _where($i): relative (0.0 - 1.0) position of sash $i
538   # _ploc($i): position in pixels of sash $i
539   # _max($i): maximum position in pixels of sash $i (0 = no max)
540   set _where(0) 0.0
541   set _ploc(0) 0
542   set _max(0) 0
543   set _min(0) 0
544
545   # calculate the percentage of resizable space
546   set resizePerc [expr 1.0 - ($_rPixels.0 / $_dimension)]
547
548   # now set the pane positions
549   for {set i 1; set n 0} {$i < $totalpanes} {incr i; incr n} {
550     if {$_resizable($n)} {
551       set _where($i) [expr $_where($n) + ($_frac($n) * $resizePerc)]
552     } else {
553       set _where($i) [expr $_where($n) + [expr $_pixels($n).0 / $_dimension]]
554     }
555     set _ploc($i) [expr $_ploc($n) + $_pixels($n)]
556     set _max($i) [expr $_max($n) + $_pmax($n)]
557     if {($_max($n) == 0 || $_pmax($n) == 0) && $n != 0} {
558       set _max($i) 0
559     }
560     set _min($i) [expr $_min($n) + $_pmin($n)]
561     #puts "where($i)=$_where($i)"
562     #puts "ploc($i)=$_ploc($i)"
563     #puts "min($i)=$_min($i)"
564     #puts "pmin($i)=$_pmin($i)"
565     #puts "pmax($i)=$_pmax($i)"
566     #puts "pixels($i)=$_pixels($i)"
567   }
568   set _ploc($i) $_dimension
569   set _where($i) 1.0
570
571   # finally, starting at the bottom,
572   # check the _max and _min arrays
573   set _max($totalpanes) $_dimension
574   set _min($totalpanes) $_dimension
575   #puts "_max($totalpanes) = $_max($totalpanes)"
576   for {set i [expr $totalpanes - 1]} {$i > 0} {incr i -1} {
577     set n [expr $i + 1]
578     set m [expr $_max($n) - $_pmin($i)]
579     if {$_max($i) > $m || !$_max($i)} { set _max($i) $m }
580     if {$_pmax($i)} {
581       set m [expr $_min($n) - $_pmax($i)]
582       if {$_min($i) < $m} {set _min($i) $m }
583     }
584     #puts "$i $_max($i) $_min($i)"
585   }
586 }
587
588 # ------------------------------------------------------------------
589 # PROTECTED METHOD: _startDrag num
590 #
591 # Starts the sash drag and drop operation.  At the start of the drag
592 # operation all the information is known as for the upper and lower
593 # limits for sash movement.  The calculation is made at this time and
594 # stored in protected variables for later access during the drag
595 # handling routines.
596 # ------------------------------------------------------------------
597 itcl::body cyg::PanedWindow::_startDrag {num} {
598   #puts "startDrag $num"
599   
600   set _minsashmoved $num
601   set _maxsashmoved $num
602
603   grab  $itk_component(sash$num)
604 }
605
606 # ------------------------------------------------------------------
607 # PROTECTED METHOD: _endDrag where num
608 #
609 # Ends the sash drag and drop operation.
610 # ------------------------------------------------------------------
611 itcl::body cyg::PanedWindow::_endDrag {where num} {
612   #puts "endDrag $where $num"
613
614   grab release $itk_component(sash$num)
615   
616   # set new _frac values
617   for {set i [expr $_minsashmoved-1]} {$i <= $_maxsashmoved} {incr i} {
618     set _frac($i) \
619       [expr ($_ploc([expr $i+1]).0 - $_ploc($i)) / ($_dimension - $_rPixels)]
620   }
621 }
622
623 # ------------------------------------------------------------------
624 # PROTECTED METHOD: _configDrag where num
625 #
626 # Configure  action for sash.
627 # ------------------------------------------------------------------
628 itcl::body cyg::PanedWindow::_configDrag {where num} {
629   set _sashloc($num) $where
630 }
631
632 # ------------------------------------------------------------------
633 # PROTECTED METHOD: _handleDrag where num
634 #
635 # Motion action for sash.
636 # ------------------------------------------------------------------
637 itcl::body cyg::PanedWindow::_handleDrag {where num} {
638   #puts "handleDrag $where $num"
639   _moveSash [expr $where + $_sashloc($num)] $num
640   _placePanes [expr $_minsashmoved - 1] $_maxsashmoved
641 }
642
643 # ------------------------------------------------------------------
644 # PROTECTED METHOD: _moveSash where num
645 #
646 # Move the sash to the absolute pixel location
647 # ------------------------------------------------------------------
648 itcl::body cyg::PanedWindow::_moveSash {where num {dir ""}} {
649   #puts "moveSash $where $num"
650   set _minsashmoved [expr ($_minsashmoved<$num)?$_minsashmoved:$num]
651   set _maxsashmoved [expr ($_maxsashmoved>$num)?$_maxsashmoved:$num]
652   _calcPos $where $num $dir
653 }
654
655
656 # ------------------------------------------------------------------
657 # PRIVATE METHOD: _calcPos where num
658 #
659 # Determines the new position for the sash.  Make sure the position does
660 # not go past the minimum for the pane on each side of the sash.
661 # ------------------------------------------------------------------
662 itcl::body cyg::PanedWindow::_calcPos {where num {direction ""}} {
663   set dir [expr $where - $_ploc($num)]
664   #puts "calcPos $where $num $dir $direction"
665   if {$dir == 0} { return }
666   
667   # simplify expressions by computing these now
668   set m [expr $num-1]
669   set p [expr $num+1]
670
671   # we have squeezed the pane below us to the limit
672   set lower1 [expr $_ploc($m) + $_pmin($m)]
673   set lower2 0
674   if {$_pmax($num)} {
675     # we have stretched the pane above us to the limit
676     set lower2 [expr $_ploc($p) - $_pmax($num)]
677   }
678
679   set upper1 9999 ;# just a large number
680   if {$_pmax($m)} {
681     # we have stretched the pane above us to the limit
682     set upper1 [expr $_ploc($m) + $_pmax($m)]
683   }
684
685   # we have squeezed the pane below us to the limit
686   set upper2 [expr $_ploc($p) - $_pmin($num)]
687
688   set done 0
689   
690   #puts "lower1=$lower1 lower2=$lower2 _min($num)=$_min($num)"
691   #puts "upper1=$upper1 upper2=$upper2 _max($num)=$_max($num)"
692   if {$dir < 0 && $where > $_min($num)} {
693     if {$where < $lower2 && $direction != "down"} {
694       set done 1
695       if {$p == [llength $_activePanes]} {
696         set _ploc($num) $upper2
697       } else {
698         _moveSash [expr $where + $_pmax($num)] $p up
699         set _ploc($num) [expr $_ploc($p) - $_pmax($num)]
700       }
701     }
702     if {$where < $lower1 && $direction != "up"} {
703       set done 1
704       if {$num == 1} {
705         set _ploc($num) $lower1
706       } else {
707         _moveSash [expr $where - $_pmin($m)] $m down
708         set _ploc($num) [expr $_ploc($m) + $_pmin($m)]
709       }
710     }
711   } elseif {$dir > 0 && ($_max($num) == 0 || $where < $_max($num))} {
712     if {$where > $upper1 && $direction != "up"} {
713       set done 1
714       if {$num == 1} {
715         set _ploc($num) $upper1
716       } else {
717         _moveSash [expr $where - $_pmax($m)] $m down
718         set _ploc($num) [expr $_ploc($m) + $_pmax($m)]
719       }
720     }
721     if {$where > $upper2 && $direction != "down"} {
722       set done 1
723       if {$p == [llength $_activePanes]} {
724         set _ploc($num) $upper2
725       } else {
726         _moveSash [expr $where + $_pmin($num)] $p up
727         set _ploc($num) [expr $_ploc($p) - $_pmin($num)]
728       }
729     }
730   }
731
732   if {!$done} {
733     if {!($_max($num) > 0 && $where > $_max($num)) && $where >= $_min($num)} {
734       set _ploc($num) $where
735     }
736   }
737   set _where($num) [expr $_ploc($num).0 / $_dimension]
738 }
739
740 # ------------------------------------------------------------------
741 # PRIVATE METHOD: _makeSashes
742 #
743 # Removes any previous sashes and creates new ones.
744 # ------------------------------------------------------------------
745 itcl::body cyg::PanedWindow::_makeSashes {} {
746   #
747   # Remove any existing sashes.
748   #
749   foreach sash $_sashes {
750     destroy $itk_component($sash)
751   }
752   
753   set _sashes {}
754   set skipped_first 0
755   #
756   # Create necessary number of sashes
757   #
758   for {set id 0} {$id < [llength $_activePanes]} {incr id} {
759     set p [lindex $_activePanes $id]
760     if {[$itk_component($p) cget -resizable]} {
761       if {$skipped_first == 0} {
762         # create the first sash when we see the 2nd resizable pane
763         incr skipped_first
764       } else {
765         # create sash
766
767         itk_component add sash$id {
768           frame $itk_interior.sash$id -relief raised \
769             -height $itk_option(-sashwidth) \
770             -width $itk_option(-sashwidth) \
771             -borderwidth 2
772         } {
773           keep -background
774         }
775         lappend _sashes sash$id
776         
777         set com $itk_component(sash$id)
778         $com configure -background $itk_option(-sashcolor)
779         bind $com <Button-1> [code $this _startDrag $id]
780         
781         switch $itk_option(-orient) {
782           vertical {
783             bind $com <B1-Motion> \
784               [code $this _handleDrag %x $id]
785             bind $com <B1-ButtonRelease-1> \
786               [code $this _endDrag %x $id]
787             bind $com <Configure> \
788               [code $this _configDrag %x $id]
789             # FIXME Windows should have a different cirsor
790             $com configure -cursor sb_h_double_arrow
791           }
792           
793           horizontal {
794             bind $com <B1-Motion> \
795               [code $this _handleDrag %y $id]
796             bind $com <B1-ButtonRelease-1> \
797               [code $this _endDrag %y $id]
798             bind $com <Configure> \
799               [code $this _configDrag %y $id]
800             # FIXME Windows should have a different cirsor
801             $com configure -cursor sb_v_double_arrow
802           }
803         }
804       }
805     }
806   }
807 }
808
809 # ------------------------------------------------------------------
810 # PRIVATE METHOD: _placeSash i
811 #
812 # Places the position of the sash
813 # ------------------------------------------------------------------
814 itcl::body cyg::PanedWindow::_placeSash {i} {
815   if {[string compare $itk_option(-orient) "vertical"]} {
816     place $itk_component(sash$i) -in $itk_component(hull) \
817       -x 0 -relwidth 1 -rely $_where($i) -anchor w \
818       -height $itk_option(-sashwidth)
819   } else {
820     place $itk_component(sash$i) -in $itk_component(hull) \
821       -y 0 -relheight 1 -relx $_where($i) -anchor n \
822       -width $itk_option(-sashwidth)
823   }
824 }
825
826 # ------------------------------------------------------------------
827 # PRIVATE METHOD: _placePanes
828 #
829 # Resets the panes of the window following movement of the sash.
830 # ------------------------------------------------------------------
831 itcl::body cyg::PanedWindow::_placePanes {{start 0} {end end} {forget 0}} {
832   #puts "placeplanes $start $end"
833
834   if {!$_initialized} {
835     return 
836   }
837
838   if {$end=="end"} { set end [expr [llength $_activePanes] - 1] }
839   set _updatePanes [lrange $_activePanes $start $end]
840
841   if {$forget} {
842     if {$_updatePanes == $_activePanes} {
843       set _forgetPanes $_panes
844     } else {
845       set _forgetPanes $_updatePanes
846     }
847     foreach pane $_forgetPanes {
848       place forget $itk_component($pane)
849     }
850   }
851   
852   if {[string compare $itk_option(-orient) "vertical"]} {
853     set i $start
854     foreach pane $_updatePanes {
855       place $itk_component($pane) -in $itk_component(hull) \
856         -x 0 -rely $_where($i) -relwidth 1 \
857         -relheight [expr $_where([expr $i + 1]) - $_where($i)]
858       incr i
859     }
860   } else {
861     set i $start
862     foreach pane $_updatePanes {
863       place $itk_component($pane) -in $itk_component(hull) \
864         -y 0 -relx $_where($i) -relheight 1 \
865         -relwidth [expr $_where([expr $i + 1]) - $_where($i)]
866       incr i
867     }    
868   }
869
870   for {set i [expr $start+1]} {$i <= $end} {incr i} {
871     if {[lsearch -exact $_sashes sash$i] != -1} {
872       _placeSash $i
873     }
874   }
875 }